home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
A.C.E. 3
/
ACE CD 3.iso
/
files
/
docs
/
real3ddo.lha
/
Real3DPart4.doc
< prev
next >
Wrap
Text File
|
1994-11-27
|
151KB
|
4,875 lines
PART 4
- TUTORIAL 6.54 -
Figure T6-62: Parallel Waves (PICTURE: T6-62)
Play the animation and the method generates waves in "x" direction.
6.23.4 Waves and Ripples
Of course, it is possible to create more than one WAVE method at the same
level, in which case the generated waves will interfere with each other.
In other words, the result wave is the sum of the component waves. Because
the size of the parameters for wave methods defines the wave-length and
-height, this feature can be used for example for generating "ripples" on
waves.
So, let's create two wave methods generating parallel waves perpendicular
to each other.
1. Create parallel waves as described in the previous example.
2. Duplicate the method object, rotate it and make it much smaller.
Figure T6-63: Interfering Waves (PICTURE: T6-63)
Play the animation and there will be small waves in the big waves.
6.23.5 Waving Particles
This example demonstrates how the wave method can be used for waving a
set of primitives instead of a mesh.
If the target object is not a freeform object, then it is treated as a
"point" of a freeform object. Instead of using one freeform target for the
method, we can use hundreds of primitives.
So, let' s create an example where the wave method is applied to a number
of spheres:
1. Create a line consisting of small spheres.
2. Create a WAVE method to the same level with the spheres.
3. Create a coordsys and a circle under the method.
4. Play the animation.
- TUTORIAL 6.55 -
Figure T6-64: Waving Particles (PICTURE: T6-64)
- TUTORIAL 6.56 -
Chapter 7 RPL
-------------
7.1 INTRODUCTION
RPL stands for REAL 3D Programming Language. It is a FORTH-like language
integrated into the REAL 3D environment. It is a full featured programming
language which provides you with power, flexibility and total control over
all the features of REAL 3D.
The language has three main uses: as a method for storing macros, as a
file format for importing and exporting all the information needed to
describe the scenes and animations you create, and as a programming
interface to allow you to expand the functions of REAL 3D to fulfill any
requirements you might have.
Because RPL is meant to be used for expanding the REAL 3D program, it has
been designed to be as fast in its execution as possible. It is also
capable of making direct access to the Operating System. For these
reasons, it is quite a low level language. However, considerable effort
has been devoted to make it easy to learn with the intention of all users
being able to make use of it.
This section is intended to introduce you to RPL programming as easily as
possible; but some understanding of basic computer programming principles
has been assumed. If after reading through the introduction and working
through the tutorial section you do not feel you have a full understanding
of how to use RPL then there are many good books on introductory
programming. Any that directly relate to FORTH will provide the best
assistance, and most if not all the examples will work in RPL.
7.1.1 Basic Concepts
First some RPL terminology to get started:
Word
A word is a collection of constants and references to previously defined
words that are to be executed when the word is run; like a procedure or
function in the languages "C" or Pascal.
Vocabulary
This is the collection of all defined words, variables and named
constants.
Stack
This is an area of memory used by the RPL system for storing the
information it needs to process the words you want to execute and the
operands those words need for their execution.
RPL, like FORTH, is a threaded language. Threaded languages grow
incrementally; you define new words using existing words that are either
predefined (standard RPL words) or words that you have defined earlier.
RPL is interactive, it executes or compiles words the instant they are
entered. New words become a part of the RPL vocabulary as soon as their
definitions are completed. They can then be executed just by typing in
their names and pressing <ENTER>, or they can be included in the
definitions of new words. Words must be entered exactly as they appear in
the vocabulary, upper-case letters must be upper-case and lower-case must
be entered as lower-case. They must also be entered in full, they cannot
be abbreviated in any way.
- TUTORIAL 7.1 -
7.2 TUTORIAL
7.2.1 Getting Started
To start your introduction to RPL the first step you must take is to open
an RPL Window. Select PROJECT/Windows/RPL and a window very similar to an
OS shell window will open. Through this window you will pass information
to the RPL system and it can pass information back to you.
To examine the interactive nature of RPL and for you to get started try
the following simple examples. Remember to enter the words EXACTLY as
they appear in the examples!
Example 1: Print a number.
Type the following text exactly as it is shown:
2.
When you press <ENTER> RPL will respond:
2
Note:
You just asked RPL to print out the number "2". The character "." is a
predefined command or word in RPL's vocabulary which prints out an integer
value.
Example 2: Print some text.
Now enter the following line:
"hello world" PUTS
RPL responds:
hello world
Note:
The word "PUTS" (Put String) is another predefined word which printed the
text "hello world".
Before you test any further examples some explanation of how RPL operates
is necessary.
7.2.2 Stacks
RPL is a stack oriented language. This means that RPL uses stacks to store
operands, intermediate results and return addresses.
RPL uses four stacks. The Parameter Stack holds arguments for RPL words as
well as results after executing those words. The Return Stack holds
addresses of locations from where other RPL words were called, so that
program execution can return to the location from where the calls were
made. The other two stacks are only used internally. The Control Stack is
used to check that the structure of the word is correct and all flow
control structures are balanced while compiling a new word. The Vocabulary
Stack stores the references to the compiled words that form the RPL
vocabulary.
These stacks work in Last In First Out (LIFO) manner, which is explained
below in "Parameter Stack".
7.2.3 Reverse Polish Notation
RPL uses postfix-notation, also known as Reverse Polish Notation (RPN).
In RPN the arguments, or operands, are pushed onto stack first and then an
operation is applied to them. The result is left on top of the stack, and
can be used in consecutive operations.
The conversion from algebraic notation (the way you write mathematical
expressions on paper) to RPN is quite natural.
You start with the operands you would start with if you were doing the
calculation manually, and then append an operator.
Then you take other operands and apply other operations until all the
operands and operations are used. That may sound difficult but a couple
of examples should clarify the point.
- TUTORIAL 7.2 -
Example 3: Convert 12 + 5 + 2 to RPN.
1. Manually you would first add 5 to 12. Therefore you write:
12 5 +
Remember, the operands come first and then
the operator.
2. Then you would add 2 to the result of the previous addition.
2 +
Note:
Both "+" operators require two operands. The first addition uses 12 and 5
but the second "+" seem to have only one operand, the number 2. The second
addition takes the result of the first addition as its second operand.
How the result of the first operation is preserved will be explained in
the section about the Parameter Stack.
So the whole expression in RPN is:
12 5 + 2 +
3. Finally you will use the "." word to print the result; so that the RPL
text becomes:
12 5 + 2 + .
And RPL responds by printing the result:
19
Example 4: Convert 2 + 5 * 7 to RPN.
1. First you multiply 5 with 7 and then add 2 to the result.
So in RPL you enter:
5 7 * 2 + .
7.2.4 Parameter Stack
When RPL scans input, whether it comes from the keyboard or from a file,
it uses the Parameter Stack to store all operands it detects. This will
be just called "the stack" for brevity, when there's no risk of confusing
it with the Return stack.
There are three kinds of operands in the RPL system: integer , floating-
point, and address. The address operands can be addresses of RPL words, or
addresses of variables (integer or floating-point) or generic addresses,
addresses to any kind of value you want. All the addresses are handled in
the same way. That is to say that RPL makes no distinction between
addresses of different types of items.
The stack operates on the basis of the last operand in will be the first
out. This is the LIFO principle as mentioned. If you picture the stack
as a pile of bricks (or any other neatly stackable material), you put new
bricks on the top of the pile and also take them off from the top.
So how are operands put onto the stack? Just enter them, and they are
"pushed" onto the stack so that the last entered number will be the top-
most one. In Example 1, you pushed one operand onto the stack (number 2).
Then the command "." pulled it off and printed it in your RPL window.
Now push several operands onto the stack,
Try the following:
1
2
3
You pushed three numbers onto the stack. Print them all out by entering
three "." commands and RPL responds:
3
2
1
- TUTORIAL 7.3 -
Try to enter "." command once more time and RPL will respond:
STACK EMPTY
There were no more operands left on the stack for RPL to print.
You get the same error message if you, for whatever reason, try to pull a
value from the stack when none are available. This would occur if you had
two integers on the stack and entered the "+" word twice.
The second "+" has only one stack item and the error message is given.
Now examine what happened to the stack for Example 3. Diagrams will be
used to illustrate what is happening with the stack as each step is
interpreted.
1. Suppose you start with an empty stack.
2. The RPL statement "12 5 + 2 + ." was scanned by the RPL interpreter.
3. First, 12 was pushed to stack.
+----+ <- top of stack
| 12 |
+----+
4. Then 5 was pushed on stack
+----+<- top of stack
| 5 |
+----+
| 12 |
+----+
5. The "+" was scanned, which, because it is a executable word not an
operand, is executed. The word "+" pops (takes off) the first two
operands from the stack, then adds them and pushes the result back
onto the stack.
+----+
| 17 |
+----+
6. Then 2 was pushed on stack
+----+
| 2 |
+----+
| 17 |
+----+
7. The last "+" was scanned, the top two stack items are added, resulting
in the following stack situation:
+----+
| 19 |
+----+
8. Finally the "." was scanned and interpreted and the value is printed
leaving the stack in its initial empty state.
Note:
RPL words must be separated from each other and their operands by either
a <SPACE> or <ENTER>, but using <ENTER> also passes the current line to
the RPL system and it will be interpreted immediately. So the example
above could also have been entered as:
12
5
+
2
+
.
7.2.5 DataTypes
There are three kinds of data types in the RPL system: integer, floating-
point and string. These are often refered to as literals. In order to
enter values on the operand stack, you type in literals separated by
<SPACE> or <ENTER> just as you have done in the examples above.
Integers can contain only integral values, such as -3, 0 and 12, while
floating-point numbers can be used for representing decimal numbers like
-1.2, 0.12 and 1243.1.
- TUTORIAL 7.4 -
7.2.5.1 integers
Integer literals begin with a optional sign (+ or -) and contain only
decimal digits. So far you have only used integer arithmetic, with integer
literals and their corresponding integer operator words.
7.2.5.2 Floating-points
Floating-point literals are similar to integer literals but they contain
a decimal point or a exponent separator or both. The exponent separator
can be either "e" or "E". The exponant can then contain an optional sign.
As you would expect, arithmetic operations can also be carried out with
floating-point values. There are separate words to carry out these
operations:
F+ - add two floating-point values
F- - subtract
F* - multiply
F/ - divide
F. - print
Fxx - etc.
Example 5:
Enter the following text:
100.0 0.5 F* F.
RPL replies:
50.0000
It did that because you pushed two floating-point values onto the stack
then multiplied them as floating-points and finally printed out the
return value from the "F*" word, as a floating-point.
Example 6:
Now try the following:
123
F.
and RPL responds with:
123.0000
This demonstrates an important principle of RPL. RPL uses Object-
Orientation Theory to handle the operands on the stack. This means that it
knows the data type of each stack value and the data type it requires for
each operand of a word and handles them accordingly. In Example 6 above,
when the command "F." pulled the integer value 123 off the stack, it
converted the value to the required type before it tried to use it.
This also works if words requiring integer operands receive floating-point
values.
Example 7:
When you enter:
6.8
.
RPL will respond with:
7
The RPL word "." works with integers, it had to convert the floating-
point value it pulled from the stack to an integer before it printed it;
because integers cannot contain any fractional part the value was rounded
to the nearest integer value.
7.2.5.3 STRINGS
String literals begin and end with a double quote character. If a
double-quote is wanted in a string literal, it should be preceded by a
back-slash.
- TUTORIAL 7.5 -
For example:
"This string contains a \"."
Will be interpreted as:
This string contains a ".
If a back-slash is required then two must be entered consecutively.
For example:
"This string contains a \\."
Will be interpreted as:
This string contains a \.
RPL handles strings slightly differently from the way the integers and
floating-points are treated. With integer and floating-point literals
the numerical value is pushed on stack, and with string literals the
address of the first character of the string is placed on the stack.
You have already encountered the string printing word "PUTS". Let's look
at how it actually operates.
Consider the RPL text:
"Hello, this is a string" PUTS
1. When the string "Hello, this is a string" was typed, RPL did NOT push
the entire string onto the stack. Instead the address of the string was
pushed. The memory for the string itself was allocated from elsewhere.
2. The word "PUTS" then pulls the address out off the stack and copies the
string from that address to the window. Every character has a
corresponding value, which is used for representing the character, this
is called the ASCII code. For example, the ASCII code for the character
"!" is 33. So, when RPL stores the character "!", it actually stores
the value 33. The RPL word "EMIT" can be used for printing out
characters by supplying ASCII code for them as operands.
Example 8:
Enter the following:
33 EMIT
and RPL replies:
!
So, you can store a simple string onto the stack and print it out using
the EMIT function.
Example 9:
Enter:
76 80 82 EMIT EMIT EMIT
and RPL replies:
RPL
Finally , you can investigate one property of string literals that might
not be immediately obvious.
As you just learned, entering a string caused RPL to allocate the memory
for the string and push the address of it onto the stack. To be precise,
the address of the first character is pushed onto the stack. This means
that it is possible to use arithmetic operations to select part of a
string.
Example 10:
1. Type the following string:
"Hello again!"
- TUTORIAL 7.6 -
Note:
The first character of "again!" is the 6th in the whole string
2. Enter:
6 +
Note:
You have now added 6 to the address of the first character ("H") of the
string.
3.Finally, type:
PUTS
and RPL replies
again!
Some care is needed when using this "address arithmetic"; if you make an
error and change the address so that it is outside of the original string
data structure, then the results are quite unpredictable. In the worst
case you could crash the system if you read from certain hardware
registers by mistake.
7.2.6 Stack Manipulation Words
There are a number of words that change the order of the stack items. The
"SWAP" word, as it's name implies, takes two values off the stack top and
puts them back in reverse order.
Example 11:
1 5 SWAP
after 1 after 5 after SWAP
+-------+-------+----------+
| 1 | 5 | 1 |
+-------+-------+----------+
| 1 | 5 |
+-------+----------+
"SWAP" is only needed if the order of the operands affects the result. It
is unnecessary when using words such as "+" and "*" among others.
Quite often there is more than one way to enter an algebraic expression in
RPL:
Example 12: Calculate the value of
28 / (2 + 4 * 3)
1. You start from within the parentheses as you would when calculating
manually:
4 3 * 2 + 28 SWAP /
after 4 after 3 after * after 2
+-------+-------+-------+-------+
| 4 | 3 | 12 | 2 |
+-------+-------+-------+-------+
| 4 | | 12 |
+-------+ +-------+
after + after 28 after SWAP after /
+-------+--------+----------+-------+
| 14 | 28 | 14 | 2 |
+-------+--------+----------+-------+
| 14 | 28 |
+--------+----------+
2. Or, start from the left:
28 2 4 3 * + /
after 28 2 4 3 after * after + after /
+--------------+-------+-------+-------+
| 3 | 12 | 14 | 2 |
+--------------+-------+-------+-------+
| 4 | 2 | 28 |
+--------------+-------+-------+
| 2 | 28 |
+--------------+-------+
| 28 |
+--------------+
This illustrates that although you can usually manage without "SWAP", it
can quite often make
- TUTORIAL 7.7 -
it easier to enter your expressions in an understandable way.
The next useful word for manipulating the stack, is "DUP"; which, as its
name suggests, duplicates the top stack value. This is useful when you
need an entered value or intermediate result more than once.
Example 13:
Calculate
(3 + 7 * 2) / 3 + (3 + 7 * 2) * 4.
You can convert it to RPL and get:
3 7 2 * + 3/ 3 7 2 * + 4 * + .
or you can use DUP and get
3 7 2 * + DUP 3 / SWAP 4 * + .
You can try these out and see for yourself that they give the same result.
To get a copy of the second stack item you use the word OVER.
Example 14:
To calculate 3 + 3 * 7 you can enter:
3 7 OVER * + .
If you need get a copy of a value deeper in the stack you use the PICK
word.
Example 15:
To get a copy of the fourth value on the stack you enter:
4 PICK
Note:
The count of stack items (i.e. 4) does not include the count itself
although it will be on the stack top when "PICK" is applied. In the
previous example 4 PICK will copy to the stack top the value which was the
fourth stack item before 4 was entered.
"ROT" rotates the three topmost stack values so that the third item will
become the top item, and the first and the second item (counting from the
stack top) will be the second and the third stack item respectively.
Example 16: Push 3, 5 and 7 onto the stack and then apply ROT and see how
it changes the stack.
3 5 7 ROT
after 3 after 5 after 7 after ROT
+-------+-------+-------+---------+
| 3 | 5 | 7 | 3 |
+-------+-------+-------+---------+
| 3 | 5 | 7 |
+-------+-------+---------+
| 3 | 5 |
+-------+---------+
Note:
You can use the .S" word toprint the contents of the whole stack without
removing any values to see how this example progresses.
The word ROLL is used to rotate a given number (n) of stack items so that
the n'th stack item becomes the stack top item and all the items between
the first and the n'th item are moved one position deeper in the stack.
Finally there is also a word for popping a value off the stack without
printing it. The word is "DROP". You will need it later when you examine
conditional execution.
- TUTORIAL 7.8 -
7.2.7 Compiling New Words
The examples so far have only demonstrated that RPL can be used as a
slightly peculiar calculator. If it is to be used as a programming
language, RPL needs to be able to store a sequences of operations for
repeated use or which need to be executed by another part of the program.
Many programming languages call these sequences functions or subroutines.
The term "word" from the FORTH programming language is adopted for RPL.
A word is a collection of RPL code which can be recognised by its name.
This code can consist of literals, references to variables and constants,
and references to other words.
A definition of a word begins with a colon ":" continues with the name of
the new word, and ends with a semicolon ";".
When the RPL interpreter scans the colon word, a new entry in the
vocabulary is started. The name of the word and the type of entry (in
this case a "word definition") are stored.
Everything between the name and the ending semicolon is compiled as the
word's definition. When a previously defined word is given as part of the
definition, a reference to it is stored. When a literal is given, its type
(integer, float or string) and its value are stored in the entry. When a
named constant or a variable (See: Constants and Variables) is given, a
reference to its own entry is stored. In summary, the vocabulary entry for
a word is made of a name, the word type and a set of literal values and
references to previously defined words,
The following examples should help to explain:
Example 17: Define a word that prints out a specific string.
Enter:
:Prompt
"PROMPT TEXT>" PUTS
;
Remember, there has to be at least one space between ":" and the name for
the new word, because the ":" is itself a word.
User-defined words are used just like any pre-defined word. To use them
you just need to enter their name.
Example 17b: Use the word "Prompt" defined above.
Enter:
Prompt
RPL will print:
PROMPT_TEXT>
User defined words would be quite useless if you couldn't pass information
to them to control their operation. However, as mentioned above, user-
defined words are indistinguishable from the built-in words. This means
that passing operands to them is carried out using the Parameter Stack in
exactly the same way as you have passed operands to words since Example 1!
Now test this by defining your own interger printing word.
Example 18: New integer printing word.
: MyPrint
.
;
- TUTORIAL 7.9 -
Call it:
10 MyPrint
and RPL responds:
10
Or call it as follows:
10 20 100 MyPrint MyPrint MyPrint
and the reply will be:
100 20 10
Now create a function which prints numbers out twice. To do this, you have
to use the "DUP" word mentioned previously.
Example 19: Print number twice as an integer.
: My2Print
DUP
.
.
;
Now call it:
10 My2Print
and it will reply:
10 10
or if you call it as follows:
10 My2Print 20 My2Print 100 My2Print
RPL responds with:
10 10 20 20 100 100
Before the final example in this section you should learn how to use
"comments" so you can add notes to your program to inform others how it
works (or remind yourself at a later date). The word "(" tells RPL to skip
all following text up to the next ")" command or until the end of the
current line. Because "(" is a word, you have to separate it from the text
that follows with a space.
Comment Examples:
1.
( this is a comment )
2.
( these are also
( legal comments
3.
(this produces syntax error)
4.
( this is an illegal comment too )
Finally create a function, which takes three numbers as an input and
returns the integer average of them,
Example 20: Calculate the average of three numbers.
: MyAver
( add 3 numbers and divide result by 3 )
+ + 3/
;
If you call it as follows:
10 20 30 MyAver .
RPL returns:
20
or try:
10 20 120 MyAver .
and the response will be:
50
- TUTORIAL 7.10 -
7.2.8 Constants and Variables
RPL has a mechanism to store and fetch values not only from the stack but
also in "constants" and "variables". A "constant" is a named entity whose
value is set when it is defined, and the value can be referenced but not
changed. A "variable" is a named entity whose value can be referenced and
used for either input or output.
Constants and variables must be defined outside word definitions. When a
constant is defined, its value is taken from the stack top. When you later
want to reference a constant you enter its name.
7.2.8.1 Constants
Constants are used for storing values that won't change. Although this
might seem to be the same as using literals, it is good programming
practice to use constants instead of literal values. Constants should be
descriptive, and this makes your code much easier to read by others as
well as yourself. When a constant is referrenced by its name, the value
is placed on the stack.
Example 21 : Define integer constant "DaysInAWeek" with the value 7.
Enter:
7 CONSTANT DayslnAWeek
Then entering:
DaysInAWeek .
prints out:
7
Example 22: Define the value for the floating-point constant "PI".
3.14159 FCONSTANT PI
7.2.8.2 Variables
When a numeric variable is defined, its initial value is set to zero. For
each numeric type (integer and floating-point) there are two words that
are used to access this value.
For integer variables these words are:
@ (fetch) - takes the address of an integer variable and returns the value
of that variable on the stack.
! (store) - takes two operands: the address of the integer variable and
the new value for the variable, and changes the value of the variable.
The address must be the top item on the stack and the new value the
second.
The address of a variable is pushed onto the stack when it is referred to,
not the value as with constants.
Example 23: Define integer variable COUNTER and store 17 into it, then
check its value.
VARIABLE counter
17 counter !
counter @ .
Examine what happened during this example step by step:
"VARIABLE counter"
1. Space was reserved for one integer value, to be accessed by the name
"counter".
"17 counter !"
2. The number 17 was pushed onto the stack.
- TUTORIAL 7.11 -
3. The address of the variable was fetched and pushed onto the stack.
4. The word "!" was executed, which takes the address of a variable off
the stack and assigns the second operand to the value at that address.
In a way, the name of a variable is the address of its value.
"counter @ ."
5. The address of the "counter" variable was again pushed onto the stack.
6. The word "@" was executed, which moves the contents from the given
address onto the stack.
7. The "." word was executed, which printed the value from the stack to
your window.
There are corresponding words for floating-point variables; which are
"FVARIABLE", "F!" and "F@".
Variables and constants can be used inside or outside of a word
definition. When used inside a word definition, only a reference to the
constant or variable is compiled in the word's dictionary entry.
The value of the constant or the address of the variable is fetched only
when the word is executed, not when it is defined.
Although the stack is object oriented and can automatically convert
between numeric types, the internal storage of integer and floating-
point variables is different. This means that words that operate on
floating-point variables, i.e. F! and F@ should not be used to access
integer variables. The same applies to floating-point variables and the
words for integer variable access (! and @).
7.2.9 Flow Control
Every programming language has to be able to alter the program flow when
certain conditions are met. It is also necessary to be able to repeatably
execute a section of code. RPL has addressed these needs by including a
versatile set of words into its basic vocabulary for controlling program
flow. The flow control words can only be used from within word
definitions.
7.2.10 Conditional Execution
It is often necessary to decide whether a section of code should be
executed or not. This is called conditional execution . The most basic
conditional execution structure is "IF..ENDIF".
The "IF" word takes the stack top value and uses it to make a decision
about how the execution is to proceed; this decision value is called a
"flag". In RPL a flag value is said to be "FALSE" if it is equal to zero
and "TRUE" if it has any other value. If the flag is TRUE, the words
between IF and the corresponding ENDIF are executed, otherwise execution
skips to the word that is immediately after the ENDIF.
7.2.10.1 Comparisons
To put a flag on the stack ready for IF, it is useful to use words that
compare stack items and as a result push a flag on the stack. Built into
RPL are the following comparison words:
Integer Float
----------------------------
= F= - is equal
< F< - is less
> F< - is greater
>= F>= - greater or equal
<= F<= - less or equal
<> F<> - not equal
Each of these takes two stack values, compares them, and places a flag
onto the stack whose value depends upon the result of the comparison.
- TUTORIAL 7.12 -
Example 24: Compare 2 and 3 to see which is less.
2 3 <
The second stack item is compared to the first stack item; the flag pushed
on the stack indicates whether 2 is less than 3. A value of 1 means
"TRUE", and a 0 means "FALSE".
after 2 after 3 after <
+-------+-------+-------+
| 2 | 3 | 1 |
+-------+-------+-------+
| 2 |
+-------+
The flag states that two is less than three (as you may have guessed).
7.2.10.2 IF..ENDIF Structure
This is the simplest form of conditional execution structure.
Example 25: Unpredictable program execution
: Maybe
RANDOM 0.5 F<
IF
"Yes," PUTS
ENDIF
"Thats it" PUTS
;
Try executing "Maybe" several times and examine its response.
Note:
This introduces the word "RANDOM" which returns a random floating-
point number between 0.0 and 1.0 when executed.
The previous example tests whether the return value from "RANDOM" is less
than 0.5. If it is, it prints out the text "Yes, Thats it" otherwise it
just prints "Thats it".
This is the explanation of the previous example. When the "Maybe" is
executed:
1. Executing "RANDOM" pushes a floating-point value onto the stack
2. 0.5 is pushed onto the stack
3. The "F<" word takes two (floating-point) operands, compares
"random<0.5" and pushes the flag back onto the stack.
4. The "IF" function takes the flag off the stack and if it is non-zero,
then the ""Yes," PUTS"" text is executed; otherwise execution skips to
""Thats it" PUTS"" after the "ENDIF".
Note:
That "IF" and "ENDIF" words must be contained within a word definition;
also that every "IF" needs one corresponding "ENDIF".
7.2.10.3 IF..ELSE..ENDIF Structure
Now you can define a word that prints only positive values, negative
values are just disposed of.
Example 26: Word to print only positive values.
: .Pos
DUP 0> ( compare operand to zero )
IF
. ( print it if greater )
ENDIF
;
If you enter:
4 .Pos
RPL returns:
4
So that seems to work.
But what happens if you enter:
- TUTORIAL 7.13 -
-2 4 3 -1 .Pos .Pos .Pos .Pos
It doesn't produce:
3 4
but nothing at all. The problem is that the word does not get rid of the
negative values, so you called .Pos four times with the same value of -1
on the stack.
You need to take some action if the value on the stack is not greater than
zero. This is handled using an "ELSE" branch where the negative value will
be dropped off the stack.
Example 26b: Correct way to print only positive values.
: .Pos
DUP 0 >
IF
.
ELSE
DROP
ENDIF
;
This demonstrates another form of the IF structure, the IF. .ELSE..ENDIF
structure.
If you now enter
-2 4 3 -1 .Pos .Pos .Pos .Pos
you get
3 4
as originally intended.
7.2.10.4 Inverting Flags
As well as the integer and floating-point comparison words, there is one
other useful word for use in conditional structures.The "NOT" word can
be used for reversing the result of a comparison.
For example:
1 0 20 <> .
and:
10 20 = NOT .
Both print out the same value of "1" because the condition is TRUE.
7.2.10.5 Nesting Conditionals
Conditional structures can also be nested. This means that they can be
stacked inside each other providing that for each "IF" of "IF..ELSE" there
is a corresponding "ENDIF".
Example 27: Use of nested IF..ELSE..ENDIF structure.
: CheckIt
DUP ( duplicate the operand so that you don't lose it
0 = ( compare it against to zero )
IF
"Zero" PUTS
DROP ( dispose of duplicate because it won't be needed
ELSE
0 > ( make comparison "is greater than" with duplicate
IF
"Greater" PUTS
ELSE
"Less " PUTS
ENDIF
ENDIF
;
- TUTORIAL 7.14 -
Now call it as follows:
-2 CheckIt
3.7 CheckIt
0 CheckIt
you should get the following response:
Less
Greater
Zero
7.2.11 Loops
In this section, the RPL words for branching back to repeat sections of
code several times will be explained. This type of control structure is
called a "loop". There are two basic kinds of loop: definite loops, which
execute a specific number of times unless interupted, and indefinite
loops, whose repeated execution is controlled conditionally in much the
same way as the IF..ENDIF structure.
7.2.11.1 Definite Loops
The basic RPL control structure for executing definite loops is
"DO..LOOP". You specify beginning and ending values, called the limits,
for a "loop variable". The ending value is first pushed onto the stack,
then the beginning value, before the word "DO". Then you put the words to
be repeated, and the structure ends with the word "LOOP".
Example 28: Printing a string 1 00 times.
: DoLoop
100 0 DO
"RPL loops in style!" PUTS
LOOP
;
DoLoop
The "loop variable", or index, starts with the starting value, and is
incremented by 1 at the end of each iteration of the loop. When it reaches
the ending value the loop is exited. There is a word for retrieving the
value of the index. Its name is "I", and it should only be used between
"DO" and "LOOP". It puts the current value of the loop variables on the
stack.
Example 29: Count up from 1 to 9 and print the index.
: OneToNine
10 1 DO
I.
LOOP
;
OneToNine
Prints:
1 2 3 4 5 6 7 8 9
The "DO..LOOP" always starts at a value and counts up by one until it
reaches the ending value. If you want to increment the index by a value
other than 1 , you can use another form of the definite loop: the
"DO..+LOOP". The "+LOOP" word takes an operand from the stack and adds
it to the index.
If the ending value is greater than the beginning value, then the loop is
exited if the loop variable becomes greater than or equal to the ending
value.
Example 30: Count from 0 to 1 0 in twos, printing the index.
: UpInTwos
10 0 DO
I.
2+LOOP
;
UpInTwos
Prints:
0 2 4 6 8
- TUTORIAL 7.15 -
If the ending value is less than the beginning value, then the
"DO..+LOOP" is exited when the loop variable becomes less than or equal
to the ending value.
Example 31: Count down by one and print the index.
: DownByOnes
10 0 DO
I.
-1 +LOOP
;
0 5 DownByOnes
Prints:
5 4 3 2 1
Note:
In this example the word "DownByOnes" takes its operands from the stack,
so you must supply them when you call the word.
Both forms of the "DO" loop execute at least once since the conditional
check is made when "LOOP" or "+LOOP" is executed. It is possible to
terminate a definite loop prematurely. The execution of word "LEAVE"
causes the loop to terminate at the next "LOOP" or "+LOOP".
Example 32: Print the squares of numbers until the square > 50.
: SquareLoop
10 0 DO
I DUP * ( square index )
DUP
50 > IF ( compare the square to 50 )
DROP
LEAVE
ELSE
.
ENDIF
LOOP
;
When execute "SquareLoop" will print the squares of I until they exceed
50.
7.2.11.2 Indefinite Loops
Unlike definite loops, an indefinite loop does not terminate after a
specified number of iterations. It terminates when a condition is met,
if ever.
All indefinite loops, just like definite loops, must lie entirely within
a single definition. The structure of the indefinite loops are:
BEGIN..UNTIL
BEGIN..WHILE..REPEAT
BEGIN..AGAIN
In each of these loops, "BEGIN" marks the beginning of the loop body,
which extends to the terminating word "UNTIL" , "REPEAT", or "AGAIN".
In the "BEGIN. .UNTIL" loop a flag is tested and removed from the stack at
the end of each repetition of the loop. If the flag is TRUE, the loop
terminates. Otherwise the loop repeats from the first word following
"BEGIN". Since the test is made at the end of the loop, the loop will
always be executed at least once.
Example 33:
: CountDown 1
BEGIN
DUP . ( print it out )
1 - ( decrement by one )
DUP 0 <= ( if TRUE, terminate loop )
UNTIL
DROP
"PANG" PUTS
;
10 CountDown1
The "BEGIN..WHILE..REPEAT" form of indefinite loop first executes the
code between "BEGIN" and "WHILE", and then a flag is tested, If the flag
is TRUE, the words between "WHILE" and "REPEAT" are executed; then
execution returns to "WHILE". If the flag is FALSE, then execution skips
to after the "REPEAT".
- TUTORIAL 7.16 -
Example 34:
: CountDown2
BEGIN
1 - ( decrement by one )
DUP ( if 0, terminates the loop )
WHILE
DUP .
REPEAT
"Bang" PUTS
DROP
;
10 CountDown2
The third form of indefinite loop has the form "BEGIN..AGAIN". Which
executes forever unless either the word "QUIT" or "EXIT" is executed.
"QUIT" terminates execution, empties all stacks and returns control to the
interpreter, while "EXIT" exits the current word.
Example 35:
: CountDown3
BEGIN
DUP .
1 -
DUP NOT
IF
EXIT
ENDIF
AGAIN
;
10 CountDown3
7.2.11.3 Nested Loops
As with conditional structures, all the loop forms can be nested, In
Examples 29 to 32 you used the RPL word "I" to obtain the value of the
loop variable; when you nest "DO. .LOOP" structures the words "J" and "K"
can be used to copy the indices of the second and third outer loops,
respectively.
Example 36: Print a multiplication table.
( print carriage return and line feed: )
: CR 13 EMIT 10 EMIT .
: multTable
6 1 DO
11 1 DO
I J * ( multiply indices
. ( and print the product
LOOP
CR
LOOP
;
multTable
Will print:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
This looks a little untidy so you can tidy it up by adding a conditional
to print a space.
Example 36b: Print a neat multiplication table.
: multTable
6 1
DO
11 1
DO
I J * ( multiply indices
DUP ( duplicate product for testing
10 <
IF ( if<10 then print a space first
"" PUTS
. ( now print product
ELSE ( two digits in product
. ( so just print it
ENDIF
LOOP
CR
LOOP
;
multTable
- TUTORIAL 7.17 -
Note:
It is a convention to indent flow control structures as shown in the
previous two examples. This does not make any difference to the RPL system
but, like comments, makes your programs easier to read and understand.
7.2.12 Words and the Vocabulary
The last example, as well as being your most comprehensive RPL program to
date also demonstrates an important feature of the RPL language. If you
define a word with the same name as a previously defined word then the
latest definition is used.
You can list the whole vocabulary of your current RPL window by entering
the word "VLIST". Before you do this use the size gadget of the window to
make it a decent size, the vocabulary is quite long.
The whole vocabulary, including the built-in words, will be printed to the
RPL window in chronological order with the latest words printed first. You
should see that the last two words are both "mult_table".
It is possible to remove word definitions from the vocabulary using the
word "FORGET".
Example 37 : Remove neat "multTable" from vocabulary.
FORGET mult_table
If you now execute "multTable" you will get the original untidy version.
"FORGET" actually removes ALL the words after the specified word. So,
assuming you have worked through all the examples, if you enter:
FORGET Prompt
Then all the words you have defined during this tutorial will be deleted.
It is important to note that the RPL system makes NO DISTINCTION between
the built-in words and words defined interactively or by loading a file
(see next section). This means that if you create a word definition using
the name of one of the built-in words then your new definition will
replace the original. This can be quite significant if you replace an
important or frequently used word.
You can of course recover the situation using "FORGET"; which raises
another important point: you can delete built-in words just as easily as
your own. This makes it possible to create a situation where RPL does not
have the vocabulary to do anything! Closing the window and opening a new
one enables you to start with a fresh RPL environment.
7.2.13 Loading a File
Entering RPL definitions directly to the window is acceptable for the
short examples so far in this tutorial, but it is hard to recover from
mistakes without having to completely re-enter a definition. Using a text
editor you can write any RPL code to a file and then load that file into
your RPL window.
Example 38:
Write the following RPL program using your favourite text editor and save
it as "ram:test.rpl"
"loading..." PUTS
: CR
13 EMIT 10 EMIT
;
: Hello
"Hello world" PUTS CR
;
"Done" CR
- TUTORIAL 7.18 -
Now type the following program:
"ram:test.rpl" LOAD
RPL responds:
loading ... Done
Now the words CR and Hello are defined and you can call them. Type:
Hello
and RPL responds
Hello world
7.2.14 Using RPL windows
You open a RPL window by selecting PROJECT/Windows/RPL. You can have many
RPL windows if you want. RPL windows are similar to OS "Shell" windows.
You can use <RAM>C and <RAM>V to cut and paste text to and from the
"clipboard" and the cursor keys can be used to edit your text and even
fetch previously entered text from the window "history" buffer.
Each window is separate so that whatever words are defined in one window,
cannot be used in other windows. Of course you can define identical words
in different windows. The stacks are also specific to each window.
It is possible though to use words defined in another window by
establishing a parent-child link between two windows. This is done
using the word INHERIT . The syntax for this word is:
"name_of_RPL_window" INHERIT
Example 39:
1. Define a word "HiThere" in your current RPL window
: HiThere
"Hi There" PUTS
;
2. Open a new RPL window and try to execute the word "HiThere":
HiThere
RPL responds
HiThere ?
because it didn't recognize the word.
3. Now enter the following line:
"RPL" INHERIT
4. Try HiThere again and RPL responds "Hi There"
From now on interactive commands, as well as new definitions entered in
the "RPL.1" window search the vocabulary of "RPL.1", and then, if the word
is not found, the vocabulary of "RPL". So if you have defined identical
words in both windows the definition in "RPL.1" will take priority.
Note:
The INHERIT word actually takes an address as its operand, and this
address points to the string containing the name of the RPL window whose
vocabulary you wish to INHERIT Remember by entering the string between the
double-quote marks its address was pushed onto the stack as an operand.
- TUTORIAL 7.19 -
7.3 USING RPL FOR CUSTOMIZING EDITOR
In this section we will show you how to use RPL to customize and expand
the user interface and functionality of REAL 3D.
The real power behind the customization is based on the fact that
customization allows persons to develop working environments which suit
as well as possible their different modelling needs, styles, and tools.
Other people can take advantage of these additional tools etc developed
by somebody else.
7.3.1 Communicating with RPL Programs
Before we can go any further, let's take a look how RPL programs can
interact with the user and vice versa.
RPL contains sets of words which can be used for requesting information
from the user.
Open one View window and write the following program:
: AreYouSure
"YeslNo" "Are You Sure?" GET_ KEY
IF
"You are sure" PUTS
ELSE
"You are not sure" PUTS
ENDIF
;
AreYouSure
When you call the word by typing its name, REAL 3D opens a requester with
the header text "Are You Sure". Depending on which gadget you click RPL
prints out either the text "You are sure" or "You are not sure". So, the
word GET KEY takes two parameters: a string defining gadgets to be created
and the header text for the requester. It returns a value corresponding
to the selected gadget. Gadgets are separated with the character "l" and
it is possible to create any number of them (well, there should be at
least one).
Write the following program:
: GetKeyTest
"FirstlSecondlThirdlCancel"
"Select One of These" GET_KEY
.
;
Now call the word GetKeyTest several times and see what values it returns.
The value corresponding to the rightmost gadget is always 0 (right
selection is used for negative choice, hence the value 0). The value
corresponding to the leftmost gadget corresponds to the value of 1 and is
incremented gadget by gadget from left to right.
The word GET STR can be used for requesting strings from the user, such as
object names. It takes three parameters: the address of the buffer to hold
the string, the maximum length for the string and the header text for the
requester.
Write the following program:
16 STRING ObjName
: ObjCreate
ObjName 1 5 "Create Object" GET _STR
IF
wOT_OR ObjName "CEND" C_LEVEL DROP
ENDIF
;
ObjCreate
/*
Get string from the user
*/
- TUTORIAL 7.20 -
This word opens a requester which allows you to enter the name for the
object. If you select the gadget OK, the word creates a level object with
the name you defined.
The word GET_FLT allows you to request numerical data from the user. It
takes two parameters: the address of the buffer (variable) to hold the
defined value and a title for the requester.
For example:
FVARIABLE Length
: GetLen
Length "Define Length" GET_FLT
IF
Length F@ F.
ENDIF
;
GetLen
This program asks user to define "Length" and prints it out.
Its time to leave these "user interface" words now. There are also words
available for requesting file names and vectors etc. These words can be
used in a pretty similar way to those we have already discussed. Consult
the "Reference Manual" whenever you need to use them.
7.3.2 "Master" ENVIRONMENT
When R3D2 is first started one RPL environment is created. Unlike RPL
windows this has no window associated with it.
"Master" environment is responsible for taking care of the following
things:
1. Processing of AREXX commands. All ARexx commands arriving to ARexx
port of REAL 3D are directly passed to RPL interpreter.
2. Executing MACROS. For example, whenever you select the menu Project/
Macros/Execute_Current, the following program is passed to Masters
RPL environment:
"t:macro.rpl" LOAD
3. Processing key bindings. When you press any key, that key with the
prefix "KEY_" is passed to Masters RPL interpreter. For example, when
you press the key "k", then the RPL word "KEY_k" attempts to execute.
If such a word cannot be found, the display is flashed as an error
indicator.
4. Processing RPL programs bound to icons in the Tool windows.
7.3.3 Binding Macros to Keys
Any RPL program can be bound to any key by defining a word which has the
same name as the key in question with the prefix KEY_. The word must be
defined in Master's RPL Environment and it must return information with
which windows should be refreshed. This return value consists of flags
corresponding to each window type. Constants for these are defined in the
file "editor.rpl".
IWR_SELECT
IWR_VIEW
IWR_MAT
IWR_ANIM
IWR_COL
IWR_ALL
How to define words to "Masters" environment? There are three possible
ways to do it:
1. Write a RPL program using any text editor, save it to a file and
execute that file as a Macro.
2. Insert your RPL program to the file "s:rpl-startup".
- TUTORIAL 7.21 -
3. Send your RPL program to ARexx port of REAL 3D.
Let's write a simple RPL program and bind it to a key.
1. Add the following RPL program to the end of the file "s:rpl-startup".
: KEY +
1 O_LOCK ( lock object data exclusively
( fetch selected objects on the stack:
O_GETSEL
0.1 0 0 0 M_MOVE
0 O_LOCK (free lock
IWR_VIEW ( only View must be refreshed
;
2. Start REAL 3D and create some objects.
3. Select one of the created objects and press the key "+". This causes
"Master" to call your KEY + word which moves the selected objects to
the positive direction of "x" axis.
Of course this is very stupid thing to do. So, let's improve it by asking
the user how much the selected objects should be moved.
1. Exit the Real and load s:rpl-startup file to your text editor.
2. Modify the KEY + word so that it looks like the following one:
"vectors. rpl" LOAD
VVARIABLE vMovement
: KEY +
vMovement "Move Selected Object"
GET_VECT
IF
1 O_LOCK
O_GETSEL
V@ vMovement 0 M_MOVE
0 O_LOCK
ENDIF
IWR_VIEW
;
Now, start Real, create some objects, and press the key "+". REAL 3D opens
a requester which allows you to enter three values defining how much to
move selected objects. If you exit the requester with OK, all selected
objects will be moved.
Note:
As a matter of fact, this is very handy macro because the requester allows
you to define the formulas which are then automaticaly evaluated.
1. Press the key "+".
2. Type the following formula to the first gadget:
10*3.14/100
and press the enter key. Your formula is evaluated and replaced with the
result:
0.314
3. Click OK and the macro moves all selected objects 0.314 units along
the "x" axis.
One very powerful feature provided by REAL 3D is ability to open several
screens. For example, you can use one HAM screen for rendering images and
another four color hires screen for modelling. The following macro tries
to bring the "Palette" window to the front, and if there are no Palette
windows, it opens one. This kind of macro can be very useful when you have
a lot of screens open.
( Bring window to front or )
( open it if it doesn't exist )
: KEY_8
0 "Palette" iWM_ACTIVATE WND_SENDMSG
IF
iWT_PALETTE "Palette" 200 0 60 640
WND_OPEN
ENDIF
0
;
- TUTORIAL 7.22 -
When you press the key "8" the WND_SENDMSG word sends iWM_ACTIVATE message
to all "Palette" windows. If none was found, it uses the word WND_OPEN to
open one, This prevents you from accidentally opening more than one
"Palette" window.
The RPL "MENU" allows you to execute whatever menu through RPL. The word
takes three parameters which describes the menu to be executed.
For example, in order to execute the menu Create/Visible/Polygon enter the
following string in any RPL window: 1 0 0 MENU
then click the mouse on any View window and you can create a polygon. The
reason for this is that the "Create" menu is the second menu. Because
numbering starts from 0, the second menu corresponds the number 1.
The second parameter (0) defines the first menu item of "Create", which is
"Visibles". The last parameter (0) defines the first sub-menu, which is
"Polygon".
Naturally, you can create a word in to the rpl-startup file and call the
MENU word to activate any menu. For example, by adding the following word
to your rpl-startup file, you can activate the Lathe tool by pressing the
key "l".
: KEY_l
1 7 0 MENU ( Create/Compound_Tools/Lathe
0
;
Note:
If the menu is "Ghosted" you cannot execute it using the mouse or a word.
Because the MENU word does the same as if you selected the actual menu,
you don't have to worry about the return value.
For example, the following key binding works only if the active window is
a View window.
: KEY_q
3 0 1 MENU ( View/Type/Perspective )
0
;
The standard "rpl-startup" file contains a lot of examples of how to bind
RPL programs to keys. Take a look at it for more examples and our attempt
at a form of standardization.
7.3.4 Binding Macros to Icons
REAL 3D allows you to create your own icons on Tool windows and bind RPL
programs to them, When the icon is clicked, the corresponding RPL program
is executed.
There are two kind of bindings: RPL word bindings and RPL file bindings.
In other words, the name of the icon can correspond either to the name of
the RPL word or the name of the RPL file.
In order to create an icon which is bound to a RPL word called HI THERE:
1. Write the program containing the word HI_THERE and save it as
"Ram:test".
: HI THERE
"YeslNo" "Are You Sure" GET_KEY
IF
"You are Sure" PUTS
ELSE
"You are not sure" PUTS
ENDIF
0
;
2. Select the function Project/Macros/Execute Named and execute the macro
"Ram:test" (or insert it to your rpl-startup file and restart Real).
3. Open one Tool window.
4. Select the menu Tools/Create Icons which contains one string gadget and
two radio-buttons. Enter the string HI THERE to the string gadget and
make sure that the radio-button "Word" is selected. Click OK and one
icon is created on the Tool window with the text HI_THERE.
- TUTORIAL 7.23 -
5. Click the icon and your HI_THERE word is called causing Real to open a
requester with the title "Are You Sure".
In order to bind an icon to a file, just select the "File" radio-button
for the icon to be created and then REAL 3D attempts to load the file
whose name is the name of the gadget. REAL 3D tries to find that file from
the directory defined by Settings/Paths.
One very fast way to write a RPL program is to let the REAL 3D do it for
you, in other words, to record a macro:
1. Create one sphere
2. Activate macro recording by selecting a menu Project/Macro/Record and
move the sphere a bit to the right.
3. End macro.
4. Select the menu Project/Macro/Current_To_Named. REAL 3D opens a file
requester and allows you to define a file name for the named macro.
Type "ram:test".
5. Now create one icon on the Tool window with the name "test".
6. With the sphere selected, click the icon and your sphere is moved to
the right.
Note:
REAL 3D standard configuration contains the directory named "r3d2:macros".
If you put your macros there, the RPL interpreter can always find them.
Now we have gone through some basic concepts how to customize REAL 3D by
writing RPL procedures and binding them to keys and icons. This chapter
only scratches the surface of possibilities for this customization ability
which can offer users a good basis for learning new things and get new
ideas about how to increase your productivity. The fact is that usually
you have to spent a little time to build up a new function, but once you
get it done, it can speed up your modelling in incredible ways.
7.4 CREATING ANIMATIONS USING RPL
7.4.1 Modifying Objects Directly
The animation system of REAL 3D is very sophisticated. However, there
might be some animations which cannot be created with it, simply because
they are absolutely crazy. In this section, we will learn more about RPL
by showing basic concepts about how to create these absolutely crazy
animations which cannot be created in any other way.
The principle is that we don't use the animation system of REAL 3D at all.
Instead we will create animations by following the procedure described
below:
1. Create your scene
2. Render it
3. Save the rendered image (if not rendered to a file)
4. Test if all frames are rendered, if they are, exit
5. Do something
6. Go back to the line 2.
- TUTORIAL 7.24 -
The line 5 (Do something) really means what it says. Because the RPL is a
full featured programming language, there is not much you can't do with
it.
As a matter of fact, this is the way professional animators have created
many of their really impressive animations.
So, lets create an animation where the object just moves with a constant
speed and direction. In order to do this, you need one View, one Select
and one RPL window.
Create one rectangle, select it and enter the following lines on your RPL
window:
O_GETSEL
0.1 0.2 0.0 0 M_MOVE
REFRESH
This program first fetches the addresses of selected objects on the stack,
then moves the objects, and finally refreshes all windows.
By the way, you can enter as many commands and operands in a single line
as you wish (as long as their order is correct). For example, it is
possible to enter the previous program as follows:
O_GETSEL
0.1
0.2
0.0
0
M_MOVE
REFRESH
or as follows:
O_GETSEL 0.1 0.2 0.0 0 M_MOVE REFRESH
As you can see, moving objects are not very difficult to create. However,
they can be made even easier:
Write the following function:
: Up
O_GETSEL
0 0.1 0 0 M_MOVE
REFRESH
;
Now, make sure that the rectangle is selected and enter the following
command:
Up
RPL moves selected primitive up and refreshes your windows.
Lets create another function which moves object down. It will look much
like the first one.
: Down
0 -0.1 0 0 M_MOVE
REFRESH
;
In order to move objects up and down several times enter:
Up Down Up Down Up Down Up Down
And all objects moved up and down several times.
To make up and down stuff easier to carry out, enter the following
function:
: UpDown
Up Down
Up Down
Up Down
Up Down
;
Type UpDown and you rectangle will move up and down several times. This
way you can define useful functions and group them together.
- TUTORIAL 7.25 -
Now you know enough to create a simple animation. Create the following
program:
: AnimLeft
10 0 DO
O_GETSEL
-0.1 0 0 0 M_MOVE
REFRESH
LOOP
;
Play it and you will see that it animates all selected objects by moving
them to the left.
: AnimRight
10 0 DO
O_GETSEL
0.1 0 0 0 M_MOVE
REFRESH
LOOP
;
Call this function and it moves your objects to the right.
Lets create a function which moves objects first to the right, and then
back to the left:
: AnimRL
AnimRight
AnimLeft
;
Call this function and you will see your objects moving as you expected.
Then let's create an animation where objects this several times:
: AnimRL10
10 0 DO
AnimRL
LOOP
;
Call the "Anim10" and your objects goes back and forth ten times.
Lets create a function, which allows you to define how many times to move
forwards and backwards:
: AnimN
0 DO
AnimRL
LOOP
;
You didn't enter the upper limit for the DO function inside the "AnimM"
function. So you have to pass it to the function when you call it.
Call the "AnimN" as follows:
10 AnimN
and it goes 10 times. Call it as follows:
20 AnimN
and your objects goes 20 times.
You can use the function "I" to get the current value of the "loop index"
each time around. Let's consider the following:
: LoopTest
5 -5 DO
I.
LOOP
;
When you call this function, the result will be:
-5 -4 -3 -2 -1 0 1 2 3 4
You can use this index as a operand for something. For example, it can be
used as a "frame counter" or parameter to trigonometric functions for
defining nice motions.
- TUTORIAL 7.26 -
So, lets create an animation where all selected objects move along a sin
curve:
: SinMotion
100 0 DO
O_GETSEL
I 50 F/ ( x = I / 5 )
I 10 F/ SIN F* ( y = sin 1/10 )
0 ( z=0 )
0
M_MOVE
REFRESH
LOOP
;
Call the word "SinMotion" and all selected objects will move along a sin
curve to the positive direction of the "x" axis.
Loops can be nested. Write the following program:
: LoopLoop
10 0
DO
O_GETSEL -0.1 0 0 0 M_MOVE REFRESH
10 0
DO
O_GETSEL 0 0.2 0 0 M_MOVE REFRESH
LOOP
LOOP
;
and call it. Selected objects move ten frames to the left, then one frame
downwards and this cycle is repeated ten times.
Sometimes a programmer wants to create a loop which terminates when some
condition or event occurs. BEGIN UNTIL loop can be used for this purpose.
It repeats until a condition is TRUE.
: MyLoopTest
BEGIN
O_GETSEL 0.1 0.1 0 0 M_MOVE REFRESH
RANDOM 0.9 F>
UNTIL
;
Previous function loops until the RANDOM function returns the value which
is greater than 0.9. So the function UNTIL pushes one operand off the
stack and if it is non zero, the loop is terminated.
There are several other loops in RPL, but let's leave them for now. In the
next section you will see how to use variables in RPL. A programming
language is not a full featured programming language if it does not
support variables. So, here you go!
FVARIABLE t
: Rotate
0 t F! ( t = 0 )
BEGIN
O_GETSEL
t F@ SIN t F@ COS 0.0 0 M_MOVETO
REFRESH
t F@ 0.1 F+t F! ( t=t+0.1 )
t F@ 3.14 F>
UNTIL ( exit if t> 3.14 )
;
Now call this function and it moves all the selected objects along a
circular route with a radius of 1.
Let's improve this function a bit so that it moves the selected objects
along a circular path where the radius is passed to the function as a
operand.
FVARIABLE rad
: Rot2 ( radius )
rad F! ( store user defined radius to variable "rad" )
0 t F!
BEGIN
t F@ SIN rad F@ F* ( rad * sin t )
t F@ COS rad F@ F* ( rad * cos t )
0.0 ( z = 0 )
0
M_MOVETO
REFRESH
t F@ 0.1 F+ t F! (t=t+0.1 )
t F@ 3.14 F> UNTIL ( exit if t > 3.14 )
;
- TUTORIAL 7.27 -
Call this function as follows:
0.1 Rot2
and it will rotate all selected objects along the circular path with
radius 0.1.
Lets go through some lines of the previous function. The line
t F@ SIN rad F@ F*
does the following:
1. The address of the variable "t" is pushed onto the stack.
2. The function "F@" pulls the address of floating-point variable from
the stack, fetches the value from that address and pushes the value
onto the stack.
3. The function "SIN" takes one number off the stack and pushes the
corresponding sin-value onto the stack.
4. The address of "rad" variable is fetched onto the stack
5. The function "F@" pushes the value from that address onto the stack
6. The function "F*" takes two numbers off the stack, multiplies them and
pushes the result back onto the stack. Later this value is passed to
function M_MOVE, which moves selected objects in x direction
accordingly.
Thats all for variables now. Just remember, when you type the name of any
variable the pointer to the variable is returned onto the stack. If you
type the string, the pointer to the string is returned onto the stack.
So far we have created just wire frame animations. Lets create a complete
ray traced animation:
Write the following word:
: RayTrace
10 0 DO ( ten frames )
O_GETSEL
0.1 0 0 0 M_MOVE
RENDER
LOOP
;
Now we use the word RENDER instead of REFRESH causing REAL 3D to use ray
tracing instead of wire frame rendering. However, this animation does not
yet save the rendered images. The word SCR_SAVE can be used for saving the
screen containing rendered image.
The following shows the complete program for creating an animation. Note
that this example assumes that your View window is opened on REAL 3D
custom screen whose name is "Real.1".
100 STRING FileName
: RayTraceSave
10 0 DO
RENDER
I "Ram:test%d" FileName SPRINTF
"Real.1" FileName SCR_SAVE
O_GETSEL 0.1 0 0 0 M_MOVE
LOOP
;
The previous program uses the word SPRINT for building up the file names
for rendered images so that the name of the first image is ram:test0
and so on.
The SCR_SAVE word takes two parameters: the name of the screen to be saved
and the file name.
- TUTORIAL 7.28 -
Right, now we have created one complete animation. Although, it is very
simple (objects just moved with constant speed along an "x" axis), it
demonstrates the basic concepts needed for creating animations using this
technique. However , there is one strange thing in this example: the
result of the animation depends on the selected objects because we used
the word O GETSEL to fetch the target objects for the M MOVE word.
Although this might give your some good ideas, it is propably not what
you wanted. So, lets create such an example where all cubes move to the
right, and all rectangles move to the left. Create some cubes and
rectangles and write the following program:
100 STRING FileName
: RayTraceSave
10 0 DO
RENDER
I "Ram:test%d" FileName SPRINTF
"Real.1" FileName SCR_SAVE
"Root/cube*" O_FINDWILD 0.1 0 0 0
M_MOVE
"Root/rect*" O_FINDWILD -0.1 0 0 0
M_MOVE
LOOP
;
Right, lets leave this section now and take a look at how to create
animations using the actual Animation system of REAL 3D.
7.4.2 Creating New Methods
In this section, we will demonstrate how to expand the animation system
of the REAL 3D by writing new animation methods.
As you already know, the animation system of REAL 3D is based on so
called methods. A method is a procedure which can be associated with
objects and which makes the object "intelligent enough to know what to
do when the user plays with the time controlling gadgets.
So, when do you have to create a new method?
Only if there are no suitable methods available. It is so easy to create
new animation methods, it is possible that somebody else has already
created a method that solves your problem (Check the REAL 3D Support BBS
for files 519-436-0140 Canada 1200 to 38400 Baud 24 hrs). Sometimes it is
also possible to get the desired result by "customizing" existing methods.
So, if there is no suitable method available and you cannot create such by
customizing existing ones, then you probably have to write your own.
Let's try it.
7.4.2.1 Do Nothing Method
In this example we will learn how to create a new a method which actually
does just nothing. The purpose of this kind of silly example is just to
demonstrate the entire concept of how to attach procedures to objects in
order to make them intelligent.
So, start your favourite text editor program, type in the following RPL
program and save it as RAM:test.rpl.
:
DoNothing
1
;
& DoNothing "DO NOTHING" MTH_CREATE DROP
Then select the menu Project/Macros/Execute Named and execute the file
RAM:test.rpl. This installs the new method to the method list of Real.
Select the menu Create/Structure/Method. Real opens a requester which
allows you to define the type of the method to be created. The last method
in the list should be DO NOTHING method. So, select it, click OK and you
have just created one intelligent level object.
Now create one target object for the method and play the animation. As
you can see, it really does nothing.
- TUTORIAL 7.29 -
There is only one requirement for the object procedure: it must return
either 1 or 0 (TRUE or FALSE). The return value 1 indicates that the
method in question succeeded in its mission. The return value 0 indicates
that something went wrong and the animation should be cancelled. Our
procedure "DoNothing" always returns 1 because nothing really can go
wrong.
The last line of the program
& DoNothing "DO_NOTHING" MTH_CREATE DROP
then installs our method to the method list of Real. The MTH_CREATE takes
two parameters: the address of the RPL word and the address of the name
for the method. So, the first command "&" just pushes the address of the
"DoNothing" word onto the stack.
If the creation succeeded, the MTH_CREATE word returns the address of the
created method. We are not interested in this adress so it is discarded
with the word DROP.
Because the DoNothing procedure is really stupid, it is easy to make it
smarter.
Add the following line to your "s: rpl-startup"
"ram:test.rpl" LOAD
This installs your method automatically when you start Real.
Now modify the contents of the ram:test.rpl file so that the word
DoNothing actually does something:
: DoNothing
"YeslNo" "Continue" GET_KEY
;
Restart Real, create one DO_NOTHING method and play the animation.
Requester is opened with the header text "Continue" and two gadgets "Yes"
and "No". If you select "No" gadget, the GET_KEY returns 0 and the
animation is terminated. If you select "Yes" gadget, the GET_KEY returns
1 and Real proceeds to the next frame asking you the same question again.
Before we leave this DO_NOTHING example, let's do something useful. Let's
create one real animation method which moves the target objects randomly.
So, modify the "ram:test.rpl" file so that the DoNothing word looks like
the following.
: DoNothing
0 o2 @ RANDOM RANDOM RANDOM 0
M_MOVETO
1
;
Now, restart Real, create one DO_NOTHING method with some targets and play
the animation. Now we have finally managed to get something which looks
like an animation.
The reason why we first showed these silly "do nothing" examples was that
it is important to realize that it is totally up to the user to decide
what his/her object procedures (methods) should do. They can be simple or
complex and tailored to address specific needs of the user.
7.4.2.2 Move Absolutely Along a Path
In this example, we will demonstrate how to create a method which moves
target objects along a path so that if the target is dislocated from the
path, it is immediately pulled back to it. This is very good example,
because it demonstrates how powerful a tool RPL is and how user created
methods expand the functionality of the program.
The first thing to do is to decide what kind of properties our custom
methods should have and what kind of parameteres it requires. In this
case our method strongly resembles the PATH methods.
So, the syntax diagram of the method looks like the following:
+--------+
| Parent |
+--------+
/ \
+--------+ +----------+
| target | | Level(M) |
+--------+ +----------+
\
+------+
| Path |
+------+
- TUTORIAL 7.30 -
Next we have to work out what the procedure to be created should do. In
this case, it should do the following things:
- Read the current time
- Evaluate one point from the parameter object corresponding to the
current time.
- Move target object(s) to the evaluated point.
So, when the time runs from 0 up to 1, the evaluated points move along the
parameter object from its beginning point towards its end point
accordingly pulling all target objects with it.
The following program defines AbsPath method procedure and adds a new
animation method to the Animation System.
( Load definitions for object data structure "objects. rpl" LOAD
( One general usage support word
: GetTargets
0 ( 0 terminates the list
o2 @ O_GETSUB ( first subobject
BEGIN
( test if the object address is not zero
DUP IF
( fetch the contents of wMETHOD field:
DUP O.wMETHOD W@
NOT IF ( if zero, valid target
DUP
ENDIF
ELSE ( object address was zero, so
EXIT ( no more targets, exit the word
ENDIF
( fetch the address of the next object:
O_GETNEXT
AGAIN
;
( Actual object procedure
: AbsPath
GetTargets ( fetch target objects on the stack
o1 @ O_GETSUB ( find the parameter object
DUP NOT IF ( if no parameter, syntax error
"Parameter Object Missing" PUTS
0 ( cancel animation
EXIT ( exit word
ENDIF
t F@ u F@ v F@ O_EVAL 0
M_MOVECOG
1 ( return 1, everything is okay
;
( Install new method to the animation system
& AbsPath "ABS_PATH" MTH_CREATE DROP
As you can see, it takes only some lines of RPL code to define a
completely new animation method. After executing the program, one new
method called ABS_PATH can be seen in the Create/Structure/Method
requester.
The word GetTargets is a general usage word which can be used by all your
custom methods. It just fetches the addresses of all target objects on the
stack and terminates the list with 0 as required by M_XXXX words.
The variables "o1", "o2" and "t", "u", and "v" are automatically defined
by the animation system. "o1" contains the address of the method object,
"o2" contains the address of the parent object of method and t, u and v
variables reflects the new time for the method.
So, let's take a look at what the actual method procedure does.
Whenever the time is changed, the animation system calls our procedure
AbsPath.
As already mentioned, the word:
GetTargets
just pushes the addresses of all target objects on the stack. Then the
line
o1 @ O_GETSUB
- TUTORIAL 7.31 -
fetches the address of the parameter object on the stack (parameter object
is sub object of the method object).
The code:
DUP NOT IF
"ERROR: Parameter Object Missing" PUTS
0 EXIT
ENDIF
then tests the return value of O_GETSUB and if 0, proper error message is
printed out and 0 returned indicating that the method failed. The return
value of 0 causes animation system to stop.
Otherwise the code after ENDIF is executed:
t F@ u F@ v F@ O_EVAL 0 M_MOVECOG
1
which evaluates a point from the parameter curve and moves all targets to
that point. Finally the value 1 is pushed on the stack, which indicates
that method succeeded in its task.
7.4.2.3 Chain
In this example, we will create a method which attempts to keep the
distance between subsequent target objects constant. The distance between
targets can be defined by associating a tag with the method object. If
the tag does not exists, then our method uses default value, say 0.5.
This method can be very powerful with some particle system oriented
methods like radial force etc.
Our methods should do the following things:
1. Find the tag value of "FDIS" tag associated with the method object.
2. Loop through all targets and move them so that the distance between
COGs of subsequent targets equals the fetched tag value.
Note:
That we use vector operations here.
Note:
That this requires some basic knowledges of vector operations like how
to add and subtract vectors.
( Useful support function Distribute
FVARIABLE fLen
VVARIABLE vTmp
: Distribute ( 0 aObjects .... fDist )
fLen F!
iOP_COG O_PROP vTmp V!
BEGIN
DUP
WHILE
DUP iOP_COG O_PROP
vTmp V@ VSUB VNORM
fLen F@ VMUL
vTmp V@ VSUB vTmp V!
0 SWAP vTmp V@ 0 M_MOVECOG
REPEAT
DROP
;
( Actual Object Procedure
: OP_Chain
GetTargets
o1 @ "FDIS" O_FINDTAG DUP ( find tag FDIS
IF ( if found, fetch its value on the stack:
4 + @ F@
ELSE
DROP 0.5 ( else push 0.5 on the stack
ENDIF
( call distribute with objects and distance:
Distribute
1
;
& OP_Chain "CHAIN" MTH_CREATE DROP
- TUTORIAL 7.32 -
Again, we divided our method into two different words: Distribute and the
actual object procedure OP_Chain. The reason for this is that
"Distribute" is in general usage a useful word and it is a good
programming practice to write modular programs by subdividing bigger tasks
to smaller ones. For example, your can apply the "Distribute" now
interactively to selected objects by defining just a core word which uses
the GET_FLT and O_GETSEL words for fetching the required parameters for it
instead of using O_FINTAG and GetTargets as we did here.
The Distribute words take the following parameters:
- object list terminated with 0
- distance
The first line
fLen F!
Just assigns the passed distance parameter to the variable "fLen". This
makes it easy to use all over because we don't have to remember where
it is in the stack.
The next line starts the actual job:
iOP_COG O_PROP vTmp V!
O_PROP word is first used for fetching the COG of the first object on the
stack and assigns it to the vector variable vTmp.
Then in the beginning of the loop:
DUP iOP_COG O_PROP
fetches the COG of the second object on the stack.
The line
vTmp V@ VSUB VNORM
fetches the COG of the first object on the stack, subtracts the COG of the
second object from the COG of the first object and finally normalizes the
result vector so that the lenght of it will be 1.
The line
fLen F@ VMUL
then multiplies the unit vector by given distance. Now the length of the
vector is given distance and it points from the second object towards the
first one. Then we have to subtract it from the COG of the first object
and we get the position for the second object. The following line
subtracts the vector from the COG of the first object and saves the result
back to the vTmp.
vTmp V@ VSUB vTmp V!
Then we just move the second target object to the defined point.
0 SWAP vTmp V@ 0 M_MOVECOG
There is not much to tell about the actual object procedure OP Chain. It
uses the word "GetTargets" to fetch the addresses of target objects on the
stack, the distance to be used from the tag FDIS (or uses the value 0.5 if
the tag does not exists) and then calls the word Distribute which does the
actual job. Because the method cannot fail, it always returns 1.
Before we leave this method example, let's take a look at how the
Distribute word can be used interactively.
1. Open one RPL window and define the word Distribute.
2. Create 10 spheres resembling pearl necklace.
3. Write the following program:
FVARIABLE fDistance
: Distr
fDistance "Define Distance" GET_FLT
IF
O_GETSEL fDistance f@ Distribute
ENDIF
;
4. Select all spheres and call the word "Distr".
- TUTORIAL 7.33 -
7. 4.2.4 Strange Force
In this example we will create a method object which applies a very
strange force field over targets causing the velocity and the spin of
target objects to be changed . This is an absolutely crazy method, because
the way we modify the velocity is absolutely random and it might be quite
a difficult job to find such a force field in the real world. However, it
demonstrates well the concept of designing new physical based methods and
how to create and update object tags.
First we need a word which defines a direction and the strength of the
force field.
: GetStrangeForce
RANDOM RANDOM RANDOM
;
Because we separate the word which defines the strength and direction for
the force, it is very easy to change this method just by modifying this
word.
Then we need a word which attemps to read the value of velocity tag. If
the VVEL tag does not exist, then it creates it. If the velocity tag
exists, then the method modifies its value according to Newtons Laws of
Motion. So, how much should the velocity of the object be changed if the
force affects the object for "dt" seconds? According to Mr Newton, the
strength of the force equals the mass of the object multiplied by the
acceleration. Mathematically the problem can be solved as follows:
F=ma => F=m*dv/dt => dv = F*dt/m
-----------
Because our force is not sensitive to the mass of the target object, we
can forget the mass (m). Hence, the new velocity can be solved from the
equation:
dv = F*dt
---------
The following word does all this:
( Word modifying the velocity )
( of a given object )
: DoStrangeVEL ( aObj )
"VVEL" O_FINDTAG DUP
( try to find VVEL tag
IF ( if found:
4 + @ ( fetch the address of tag value
DUP V@ ( fetch the velocity on the stack
( fetch the force vector on the stack:
GetStrangeForce
dt F@ VMUL ( dv = F* dt
( add "dv" vector to original velocity:
VADD
( assign result back to the velocity tag:
4 ROLL V!
ELSE
DROP DUP "CEND" 0 0 0 "VVEL"
O_CREATAG DROP
ENDIF
;
In order to get the new particle method perfect, also the spin must be
affected somehow. The only difference between this and the DoStrangeSPI
words is that the result vector for the VSPI tag is multiplied by 2*PI.
This is because the spin is represented in radians and 2*PI radians
corresponds to a full circle (360 deg).
( A word modifying the spin of the object )
: DoStrangeSPI ( aObj )
"VSPI" O_FINDTAG DUP
IF
4 + @ DUP V@ ( original spin
GetStrangeForce dt F@
2 PI F* F* VMUL ( delta
VADD ( new spin
4 ROLL V ( assign result to VSPI
ELSE
DROP DUP "CEND" 0 0 0 "VSPI"
O_CREATAG DROP
ENDIF
;
- TUTORIAL 7.34 -
The actual object procedure then just scans through all targets and calls
DoStrangeVEL and DoStrangeSPI words for each object.
: OP StrangeForce
GetTargets
BEGIN
DUP
WHILE
DUP
DoStrangeVEL
DoStrangeSPI
REPEAT
DROP
1
;
& OP StrangeForce "STRANGE_FORCE" MTH_CREATE DROP
7.5 USING EVAL
The EVAL word provides the user with an easy way to access data structures
of Real, such as materials and objects. In this chapter we will show you
some basic ways to use EVAL.
Create one material with all properties set to default.
How to set the brilliancy of the created material "mymat to 100? Type the
following line:
"data(mymat->spec)=100" EVAL DROP
You just set the specularity of "mymat" to 100. To make sure that this
really happened, open the material requester with the material and see.
If you don't like Reverse Polish Notation, you can use eval to evaluate
your formulas like this:
"2*3+5" EVAL .
which is the same as the phrase "2 3 * 5 + ." and RPL will reply:
11
or like this:
"2*3*sin(3*cos(123)+3)" EVAL F.
or like this:
"data(mymat->spec)=(data(mymat->bril)
+data(mymat->tran))/2" EVAL.
and RPL calculates an average of the brilliancy and transparency and
assigns these results into a specularity field.
So, let's create a material morphing animation. Create one sphere and add
material "mymat" to it by creating one texture at the same level.
Ensure that the rendering settings for the current View window are correct
(don't use the Fast-mode because it ignores all material properties).
Then write the following program:
VARIABLE rg
: MyAnim
10 0 DO ( ten frames )
I 10 * rg! ( rg=I*10 )
"data(mymat->roug)=rg" EVAL DROP
RENDER
LOOP
;
and everything is ready for the show.
Call the previous function and you'll see how the roughness of your
rectangle is morphed during the animation.
Right, how about objects. Assume that the full name (including the path)
for you rectangle is "/Root/rectangle". Then you could print out the
color of you rectangle as follows:
"data(/Root/rectangle->R)" EVAL.
"data(/Root/rectangle->G)" EVAL.
"data(/Root/rectangle->B)" EVAL.
- TUTORIAL 7.35 -
or you can change the red signal of the color as follows:
"data(/Root/rectangle->R)=120" EVAL.
or you can print out the name of your object as follows:
"data(/Root/rectangle->name)" EVAL PUTS
or you can change the line pattern of your rectangle as follows:
"data(/Root/rectangle->ptrn)=255" EVAL.
REFRESH
or you can change the register color for wire frame rendering:
data(/Root/rectangle-> reg)= 3" EVAL.
REFRESH
For more about EVAL, see the reference manual.
7.6 OBJECTS
In this section, we will demonstrate how RPL can be used for creating and
manipulating objects.
7.6.1 Object Creation
So, let's get started by creating a simple RPL program which just creates
one primitive and renders it using default rendering settings.
( A minimal program for rendering a sphere
0.0 0.0 0.0 ( center )
0.5 0.0 0.0 ( a )
0.0 0.5 0.0 ( b )
0.0 0.0 0.5 ( c )
255 0 0 0 ( RGBA )
"ellipsoid" ( name )
0 ( flags )
"CEND" ( tags )
C_ELLIPSOID DROP
RENDER
When passed to REAL 3D's stand-alone renderer, it should produce an image
representing one red sphere.
Lets go through what this program actually does.
The first word C_ELLIPSOID creates an ellipsoid. The general form of all
creation words is as follows:
Geometry Color Attributes C_nnnn Address
Geometric data is described first (in the example it consists of four
vectors: center, a, b, and c). The form of geom. data depends on the
type of the object to be created. Not all objects have geometric
description. In this example, the geom. data defines a sphere, because
the length of all three axes are the same.
( Geometric data for ellipsoid )
0.0 0.0 0.0 ( center )
0.5 0.0 0.0 ( a )
0.0 0.5 0.0 ( b )
0.0 0.0 0.5 ( c )
The second data section is a color for the object. Color is defined by 32
bits (RGB+A). Like geom. description, not all objects have color section.
( Color is defined by 32 bits R,G,B+A )
255 0 0 0 ( RGBA )
The third data section defines so called "Attributes" for objects.
Attributes can be divided into three different parts: name, flags and tag
list. The name field defines a symbolic name for the object. This name can
be used in many ways. For example, it can be used for finding the address
of the object in order to create new objects, more on this later.
The flags field is a 32 bit wide integer value. It is used for defining
on/off kinds of attributes like whether or not this object should be
"motion blurred" or "invisible". In the REAL 3D Editor, these flags can be
defined using Modify/Properties/Attributes function. For more information
about possible attribute flags, see the reference manual.
- TUTORIAL 7.36 -
The tag list can be used for defining custom attributes for objects needed
by procedural textures, animation methods etc. The tag list consists of a
number of tag identifier-value pairs. The tag value is a string whose
length is four characters which identifies the value. The type of the tag
value then depends on the tag identifier. The tag list is always
terminated by "CEND" string.
( Attributes )
"ellipsoid" ( name )
0 ( flags )
"CEND" ( tags )
C_ELLIPSOID creates an ellipsoid and returns the address. This address can
be used in many ways as we will later see. In this example, it is not
needed, so it is discarded by using the word DROP.
The last word "RENDER" then asks REAL 3D to render the defined scene which
in this example consists of only one sphere.
One basic idea of RPL is that at the lowest level all data needed for
defining objects must be passed to the RPL interface! There are no
graphical environments used for defining parts of the data needed as in
Renderman.
However , this does not mean that in the case where a scene consists of a
large number of almost identical objects the program would have to
redefine the same data again for each object. Because RPL is a full
featured programming language, there are many ways to solve this problem.
For instance, let us imagine a scene consisting of 10 spheres. Would it
take ten times more space to create such a scene as a scene which consists
of only one sphere? Only if all the objects are totally different. Usually
they are not, in which case identical data could be defined using, for
example, a separate RPL word instead of redefining it for all primitives.
Lets demonstrate this by creating an RPL program. The program would create
two identical spheres to different position.
( A simple program implementing graphics )
( environment using different RPL words )
: MY ELLIPSOID ( center addr )
0.5 0.0 0.0 ( a )
0.0 0.5 0.0 ( b )
0.0 0.0 0.5 ( c )
255 0 0 0 ( RGBA )
"ellipsoid" ( name )
0 ( flags )
"CEND" ( tags )
C_ELLIPSOID
;
0 0 0 ( center )
MY_ELLIPSOID DROP
1 0 0 ( center )
MY_ELLIPSOID DROP
RENDER
To make this clear, let's create another program. The program creates a
small "Renderman" style graphical environment used for defining
"Attribute" data section for objects to be created.
( data for simple graphics environment )
VARIABLE R
VARIABLE G
VARIABLE B
VARIABLE FLAGS
16 STRING NAME
( words for manipulating )
( graphics environment )
: RiName ( name - )
NAME CPY
;
: RiColor ( R G B - )
B!G!R!
;
: RiFlags ( Flags - )
FLAGS !
;
- TUTORIAL 7.37 -
( Ri style interface for creating )
( an ellipsoid )
: RiEllipsoid ( center, a, b, c - )
( fetch color from graphics environment )
R @ G @ B @ 0 ( A=0 )
NAME ( fetch the address of the name )
FLAGS @ ( fetch flags )
"CEND" ( no tags )
C_ELLIPSOID DROP
;
( create 3 blue spheres )
0 0 255 0 RiColor
0 0 0 1 0 0 0 1 0 0 0 1 RiEllipsoid
1 0 0 1 0 0 0 1 0 0 0 1 RiEllipsoid
2 0 0 1 0 0 0 1 0 0 0 1 RiEllipsoid
( create 2 red ellipsoids )
255 0 0 RiColor
0 1 0 0.5 0 0 0 1 0 0 0 1 RiEllipsoid
0 2 0 0.5 0 0 0 1 0 0 0 1 RiEllipsoid
( create one green ellipsoid, "blue" )
0 255 0 RiColor
"blue" RiName
0 -1 0 0.5 0 0 0.8 0 0 0 1.2 RiEllipsoid
This listing defines a custom interface where geometric data is defined by
the caller and the attributes are fetched from the graphics environment.
7.6.2 Object Instances
RPL allows creation of "object instances". This is a useful feature
whenever it takes less space to define geometric transformations and
modify new objects out of existing ones than defining object data itself.
The following example creates a number of spheres using this technique.
FVARIABLE Rad
VARIABLE Obj
( create first object )
: MY SPHERE ( center rad - addr )
Rad F! ( assign radius to variable "rad" )
Rad F@ 0 0 ( a )
0 Rad F@ 0 ( b )
0 0 Rad F@ ( c )
255 0 0 0 ( RGBA )
"ellipsoid" ( name )
0 ( flags )
"CEND" ( tags )
C_ELLIPSOID
;
0 0 0 0.5 MY_SPHERE Obj !
( create two object instances )
( from the first one )
Obj @ M DUPLICATE 0 SWAP
1.5 0.2 0.2 0 M_MOVE
( the second one: )
Obj @ M_DUPLICATE 0 SWAP
255 255 0 0 1 0 M_COLOR
The word MY SPHERE takes two parameters which are center and radius and
returns an address of the created sphere.
The line
0 0 0 0.5 MY_SPHERE Obj !
creates one sphere whose center is 0,0,0 and radius 0.5. The return value
from the C_ELLIPSOID is not discarded using DROP but returned so that it
can be stored into the variable "Obj".
The last two lines contain the big idea so lets go through them line by
line:
Obj @ ( fetch the address of the sphere
M_DUPLICATE ( duplicate it and leave
( its address on to the stack
- TUTORIAL 7.38 -
For the sake of efficiency, more than one object can be modified by RPL
modify words at one call. The address 0 means that there are no more
object addresses in the stack. So the following expression:
0 SWAP
just terminates the object pointer list by 0.
1.5 0.2 0.2 0 M_MOVE
then moves object instance 1 .5 along x axis and 0.2 along y and z axes.
The second instance is created by changing the color of the sphere from
red to yellow.
7.6.3 Grouping Objects Together
Objects can be grouped together to create hierarchical object trees. This
hierarchical construction of object conforms the basis for a large number
of other features of RPL.
The word C_LEVEL can be used for creating new levels into the object
hierarchy and its syntax is as follows:
Type Attributes C_LEVEL Addr
The following example creates one simple object tree using the word
MY_SPHERE created in the previous example. The structure of the tree to
be represented is as follows:
+------+
| Root |
+------+
/ \
+--------+ +-----+
| Sphere | | Sub |
+--------+ +-----+
/ \
+--------+ +--------+
| Sphere | | Sphere |
+--------+ +--------+
The following program builds up the hierarchy.
( Creating a simple hierarchical object )
wOT_OR "Root" 0 "CEND" C_LEVEL
O_CURRENT DROP
0 0 0 0.5 MY_SPHERE DROP
wOT OR "Sub" 0 "CEND" C_LEVEL
O_CURRENT
0 0 0 0.5 MY_SPHERE DROP
0 0 0 0.5 MY_SPHERE DROP
O_GETCURR O_GETPAR O_CURRENT
DROP
The first two lines
wOT_OR "Root" 0 "CEND" C_LEVEL
O_CURRENT DROP
create one object of the type wOT_OR. The constant named "wOT_OR" is
defined "objects.rpl" in file and the actual value of it is 2. By using
constants makes the type of the level more easy to understand for human
beings. It is strongly recommended to use these kind of definitions
instead of direct values. The first parameter "wOT_OR" defines how volumes
of all sub objects are interpreted. In this example, they are "OR" read
with each other.
The following fields
"Root" 0 "CEND"
defines attributes for the object and are identical with those we have
already seen in previous examples.
The word
O_CURRENT
takes one parameter, which is an address of an object and makes it the
current level so that all latter objects will be created inside it. The
word returns the address of the previous current level and we get rid of
it using the DROP word.
- TUTORIAL 7.39 -
The creation of the second level is similar to the first one except for
two things: the name of the level to be created is now "Sub" and we don't
delete the return value from the O_CURRENT. We leave it on the stack so
that we can use it later to pop back to the previous level using
O_GETPAR and
O_CURRENT words.
O_GETPAR takes the address of an object and returns the address of its
parent object. Then O_CURRENT makes it the new current level.
Althought all previous words are pretty simple and clear, our example
itself is not perhaps as clear as it could be. The reason for this is that
it shows how to built up a hierarchy at the lowest level. Again, it is not
necessary to define all parameters for C_LEVEL every time a new level is
created. We can create our own core word, which takes only the name of the
level to be created.
In the following example we will create some useful words which reduce the
size of the program and make it much more understandable.
( create level )
: ObjBegin ( name )
wOT_OR SWAP 0 "CEND" C_LEVEL
O_CURRENT DROP
;
( close level )
: ObjEnd
O_GETCURR O_GETPAR O_CURRENT DROP
;
"Root" ObjBegin
0 0 0 0.5 MY_SPHERE DROP
"Sub" ObjBegin
0 0 0 0.5 MY_SPHERE DROP
0 0 0 0.5 MY_SPHERE DROP
ObjEnd
( Now words ObjBegin and ObjEnd )
( can be used for defining hierarchy )
It is very easy to close the hierarchy levels by leaving the return
address of each created level on the stack and pull it out when it is
needed. To get the parent object of it in order to pop up one hierarchy
level. However, sometimes it is easier to build up the hierarchy first
and then create the actual primitives. For example, let's build up a
hierarchical tree which contains no primitives. The structure of the tree
should be as follows:
+------+
| Root |
+------+
/ | \
/ | \
+------+ +------+ +------+
| Sub1 | | Sub2 | | Sub3 |
+------+ +------+ +------+
/ \
/ \
+-------+ +-------+
| Sub21 | | Sub22 |
+-------+ +-------+
/ \
/ \
+--------+ +--------+
| Sub221 | | Sub222 |
+--------+ +--------+
Using the words ObjBegin/ObjEnd defined in previous example a program
would look as follows:
( Building up an empty hierarchy )
"Root" ObjBegin
"Sub1" ObjBegin
ObjEnd
"Sub2" ObjBegin
"Sub21" ObjBegin
ObjEnd
"Sub22" ObjBegin
"Sub221 ObjBegin
ObjEnd
"Sub222 ObjBegin
ObjEnd
ObjEnd
ObjEnd
Because we discarded all addresses of created objects using the DROP word
in the ObjBegin word, we don't remember any addresses of any objects. So,
how do we create something inside the desired hierarchy level? The address
of any object can be found by its name. For example, we could create a
sphere inside the object named "sub222" as follows:
- TUTORIAL 7.40 -
( find the address of object )
"/Root/Sub2/Sub22/Sub221" O_FIND
O_CURRENT DROP ( make it current )
( create one sphere )
0 0 0 0.5 MY_SPHERE DROP
Another possibility is to use variables to store object addresses instead
of fetching addresses using the O_FIND word.
7.6.4 SphereMan
As a final example for this RPL tutorial, we will go through how to create
"freeform objects" consisting of thousands of particles. For example, a
body of the human being can be created using thousands of spheres
distributed evenly over the body instead of using freeform meshes.
This kind of task where we have to deal with thousands of objects would be
almost impossible task to do without programming language.
In this example we will use a SIMPLE SKELETON method to pull the sphere
mass on the freeform mesh representing a head. RPL is used for creating
all spheres as well as defining positions for each sphere so that they
will be scattered all over the head.
Clear the Settings/Creation/Auto_Index menu. This dramatically speeds up
the creation process. It might be a good idea to switch the Undo feature
off.
Write the following program using your favourite text editor and save it:
: CrSpheres ( iHowmany )
1 O_LOCK
0 DO
0 0 0 ( center )
0.05 0 0 ( a )
0 0.05 0 ( b )
0 0 0.05 ( c )
255 255 255 0 ( RGBA )
"ell" ( name )
0 ( flags )
"CEND"
C_ELLIPSOID
NOT IF
"Not Enough Memory" PUTS
0 O_LOCK
EXIT
ENDIF
I.
LOOP
0 O_LOCK
"Done" PUTS
;
Now open one RPL window and load the program in. This just defines the
word CrSpheres.
As you can see the word CrSpheres takes one parameter which is the number
of spheres to be created. Depending on the memory available, your can call
this word with a different amount of spheres. If you have six megabytes of
RAM memory installed, you should be able to create about 1000 spheres. So
call the CrSphere as follows:
1000 CrSphere
This creates 1000 spheres.
Then we have to define the positions of the spheres on the skeleton mesh.
If you are familiar with the skeleton methods, you know that the tag VPHS
is used for defining at which position of the skeleton the target object
in question is pulled. Because we are going to use a freeform mesh (a
head) as a skeleton object, we have to define the first two components of
the VPHS tag. For example, if the value 0,0 corresponds to the left top
edge of the mesh, then the value 1,1 corresponds to the right bottom edge.
The value 0.5 0.5 corresponds to the middle point of the mesh etc.
- TUTORIAL 7.41 -
So, write the following program (or just load it in from the rpl
directory):
( A program scattering spheres evenly )
( over a skeleton mesh )
VARIABLE iWidth
VARIABLE iHeight
: DefPhs ( aParent iWidth iHeight )
iHeight!
iWidth !
O_GETSUB DUP NOT IF
"No subobjects" PUTS
EXIT
ENDIF
iHeight @ 0
DO
iWidth @ 0
DO
DUP "CEND" I iWidth @ F/J iHeight @
F/ 0.0 "VPHS" O_CREATAG
NOT IF
"Cannot create tag" PUTS
EXIT
ENDIF
"." PUTS
O_GETNEXT DUP NOT IF
"AII Done" PUTS
EXIT
ENDIF
LOOP
LOOP
;
The word DefPhs takes three parameters: the address of the object
containing all spheres, the number of spheres in "u" direction and the
number of spheres in "v" direction. For example, if the Width parameter
is 100, there will be 100 spheres in the "u" direction of the mesh.
Now, make the object containing all created spheres the current level
and enter the following command:
O_GETCUR 35 35 DefPhs
This defines the VPHS tag for each sphere in the current level so that
each cross-section curve of the head will consist of 35 spheres.
Create one SIMPLE SKELETON method beside all the spheres and add the
following tag to it:
ISKE 3
This tells the skeleton method that the VPHS tags are already created and
are up to date. If you forget to add this tag, the method will
automatically define tags according to the current positions of the target
objects.
Load your freeform mesh representing a head inside the method object. Now
the hierarchical structure of your object should be as follows:
+------+
| Root |
+------+
/ \
+--------+ +-------------+
| o1..oN | | Skeleton(M) |
+--------+ +-------------+
\
+---------------+
| B-Spline_Head |
+---------------+
So, spheres (o1 ... oN) are pulled on the B-Spline_Head object by the
skeleton method.
Your "SphereMan" is now ready. Select the menu Animate/Control/Refresh and
all the spheres are pulled on the head. Make the skeleton object invisible
if you don't want to see the mesh itself.
We will stop here, but you can carry on by animating the skeleton mesh.
This also causes the spheres to be animated (because whatever you do to
the head object, the spheres are always pulled on its surface). For
example, you can add one WAVE method beside the B-Spline_Head and the
sphere head will wave.
+------+
| Root |
+------+
/ \
+--------+ +-------------+
| o1..oN | | Skeleton(M) |
+--------+ +-------------+
/ \
+---------------+ +---------+
| B-Spline_Head | | Wave(M) |
+---------------+ +---------+
/ \
+------+ +-------+
| axis | | curve |
+------+ +-------+
- TUTORIAL 7.42 -
Now the spheres (o1 ... oN) are pulled on the B-Spline_Head which is
morphed by the Wave method.
Or you can use this technique just as a creation tool and delete the
skeleton after all spheres are pulled on it. Then you can use the sphere
as a beginning situation in particle animations.
The possibilities are infinite and the only existing limit is your
imagination.
- TUTORIAL 7.43 -
REFERENCE
---------
Chapter 1 MENU FUNCTIONS
------------------------
The following syntax is used to describe all the functions available
by menu selection:
/ - SUB-MENU ITEMS FOLLOW
* - TOGGLE GADGET
1.1 PROJECT
The REAL 3D binary file format follows the IFF standard. The various
data-structures necessary to define a complete REAL 3D project are each
saved in a different data-section.
Each data-structure can be saved and loaded independently. For each major
data-structure type, there is a specific function. In addition, under the
Project/ sub-menu are functions for loading and saving each individual
section.
There are two variants of loading for each data-section: "Insert" which
loads the new data and adds it to the existing data-structure; and
"Replace" which clears the existing data-structure if or when it loads
the new data.
The loader is intelligent, and can cope with loading files which contain
some or all data-sections, but each loading function will only try to
load those sections it is specified to load. The "Replace" variant of the
loading functions do NOT clear the existing data-structure if the
specified file does not contain the appropriate data-section.
The different "Save" functions only save those sections they are specified
to save.
All the functions open a file-requester to allow the user to select the
REAL 3D IFF file to load from. Changing the DOS path updates path-
settings, so it is remembered by the requester the next time it is used.
If "Confirm Save" gadget is set by using Settings/General requester, the
user is asked to confirm the operation before the old file is overwritten.
When working on a Workbench(TM) screen, Real-IFF files can also be loaded
by moving their icons onto a View window.
Objects/
Insert
Load new objects from specified file into the current level of hierarchy.
Save
Save selected objects to the specified file,
Replace
Clears the hierarchy if/when it loads the object section of the specified
file.
Project/
The user is presented with a requester to define the new name for the root
object.
New
This does the following:
1. Hierarchy cleared
2. Material Library deleted
3. Named Colors Library deleted
4. Grid Definitions removed
5. Vector stack is cleared
6. Undo Buffer cleared
7. Time and Frame Count are reset to zero
- REFERENCE 1.1 -
Insert
Load all the available sections of a project definition from the specified
file.
Save
Saves all sections of the current project definition to specified file.
Replace
Load all available sections from the IFF file and delete any corresponding
data structures in memory.
Insert Sections
A requester is opened to allow only those sections of a REAL 3D file the
user selects to be loaded. After the selection has been confirmed, the
file requester will be opened.
Save Sections
Opens requester to select data sections for saving, then the file
requester.
Replace Sections
Opens data sections requester for selecting the sections to replace from
a file.
Materials/
Material definitions are stored in the material library. They are created
and modified using a material window.
Window
Open a Material editor window. This allows materials to be created and
modified while other actions, in particular rendering, are taking place.
The material definitions are stored in a material data structure, called
the material library, complete with their material name, The material
definitions are created and modified using a material editor window.
Figure R1-1: Material Editor Window (PICTURE: R1-1)
- REFERENCE 1.2 -
Local Menu:
DEFINE/
Texture <RAM>D
Select name of image file using file requester.
Show Texture <RAM>S
Display image file. If the image is 24 bit and the External screen is
open, then a requester will be produced asking for confirmation, as
displaying the image may overwrite the contents of the External screen.
Trans. Color <RAM>C
Select Transparent color from current color.
Tags<RAM>T
Open the Tag requester to modify or define the tags associated with the
current material.
Gadgets of the Material window:
Name
Name of material in the Material library.
Texture
The path and name of an image file to use for defining various material
properties like color or transparency. Each pixel in the bit-map relates
to a point on the surface of the object, depending upon the type of
mapping control object associated with the material and its position
relative to the physical object, An index format string can be included
as part of the name. This means that the value of the index variable "i"
will be formatted to this specification and included as part of the file
name.
Spline
When this gadget is set, and the target object for the material is a
B-spline mesh, then the object itself is used for the mapping definition.
Any coordinate transformations defined by mappings associated with the
material are ignored. This means that the mapping of the texture file
follows any modifications to the shape of the mesh.
S-map uvwh
These four numeric gadgets control the position and size of the image file
when it is mapped onto a spline. The u and v values determine the position
of the top left corner of the image and w and h control what proportion of
the spline is covered. Each of these can be between 0.0 and 1,0,
Color map
Texture is to be used for material color definition.
Bump map
Red component of texture is used for bump map evaluation. The brighter the
red component, the higher the bump.
Transparency map
Green component is used for transparency evaluation. The brighter the
green component, the more transparent the material.
Brilliance map
Blue component is used for evaluating brilliance. The brighter the blue
component, the more mirrorlike the material.
Shadow map
The RGB values of each pixel of the texture file are used to modify the
current color values for the corresponding point of the objects surface,
The modification formula is:
R*r/255, G*g/255 & B*b/255
Where: r, g, b are the color values of the pixel and R, G, B are the
current color values of the surface point
It is intended for this function to be used with grayscale texture files
to artificially produce shadow effects as an alternative to using Render
Settings/Normal mode or , when it is necessary, to create artificial
shadows in addition to rendered ones.
Note:
If shadow mapping is used with a pure white object, it produces the same
results as color mapping.
- REFERENCE 1.3 -
Clip map
The surface of the object is clipped by the texture file. The object
surface is removed wherever it is not covered by the texture file. This
includes any areas not covered by the mapping or tiling, or any areas
selected as transparent.
Scope mask
If this gadget is set, the material effect is modified by using the
texture as a mask, which defines where the material is applied. Only the
points which are affected by the texture, included in the material, get
non-zero scope. The application test is equivalent to the one used for
clip mapping.
Transparent Color (Transp. col)
The color to use as the transparent color is taken from the numeric
gadgets: Trans.R, Trans.G, and Trans.B. This affects the application of a
texture for clip mapping and scope mask. Transparent color is defined in
24-bit color space, so all values vary between 0 and 255. You can
translate a color in original Amiga color system, values ranging from 0 to
15, to 24-bit system by multiplying each color component value by 16. For
example, pure white R=15,G=15,B=15 corresponds R=240, G=240, B=240.
Unshaded
Light sources and shadows do not affect the shading of this material.
Smooth
Removes specular reflections on the boundaries of transparent materials.
Exclusive
When the rendering engine evaluates the material properties for each point
on a surface, it mixes all the properties for each material defined by the
mappings at the same hierarchical level. If only one mapping is present,
then the material properties are mixed with the default material
properties.
Setting the Exclusive property means that other material definitions are
ignored for any points on the surfaces covered by that specific material.
See Also: "Effect" and "Scope" in this sub-chapter.
Tiling Gadgets
Tile
Selects whether texture file is to be tiled in X, Y or both directions.
Flip
Selects whether tiled texture file is to be mirrored in X, Y or both
directions. If set, then every second tile is flipped, making texture map
edges match better.
X-Freq. & Y-Freq.
These numeric gadgets specify the number of tiling repetitions over the
surface of the texture. If the frequency is set to 0, the texture is tiled
infinitely for all mapping types, except spherical mapping. For parallel,
both X & Y can be infinitely tiled; and for cylindrical and disk mapping,
only Y = 0 is treated as infinite tiling. When infinite tiling is used,
each tile is the size of the texture along that axis.
Grade
Selects whether color gradients are calculated for X, Y or both directions
of the texture file. Color gradients produce smooth transitions from the
color of one pixel to the color of the adjacent pixels.
Specularity
Controls how sharply defined are the high-lights reflected from the
surface of the material by light sources. The higher the specularity, the
smaller the high-light and the harder its edges.
Specular brightness (Spec. bright.)
This affects how intense the specular high-lights are.
Brilliance
The degree to which light is reflected directly from the material surface
("mirror-like" property),
Transparency
The degree to which light passes through the material surface.
- REFERENCE 1.4 -
Turbidity
Controls the degree to which light is affected as it passes through the
material. The higher the setting the denser the "fog".
Turbidity saturation (Turbid. sat.)
Defines how the distance which light travels in a turbid material affects
the light. The default value 25 gives a linear result; so if the distance
is doubled, then the effect is doubled. If this value is zero, then the
distance has no effect at all. The actual equation is:
"turbidity effect" = Turbidity *
(distance^Turbid. sat.),
^ = to the power of
Refraction
This determines the degree to which light is bent as it passes through the
material. It represents the speed of light in the material as a percentage
of its maximum speed through empty space. The higher this value the less
the light is bent.
Roughness
This controls the degree of "molecular texture" applied by the material.
This "molecular" texture is a random bump-map which is independent of
the magnification of the material.
Dither
This enables dithering of material color to be applied to individual
objects and the precise amount of dithering to be selected. The actual
amount of color deviation applied is given by:
R = R + rnd,
G = G + rnd,
B = B + rnd where -di/2 < rnd < di/2
where: di is the dither setting.
Bump height
Relative scale of bumps produced by bump-mapping texture file and
procedural bump handler.
Effect
All materials at the same hierarchical level are mixed together. This
controls how strongly the properties of the current material will affect
the objects to which it is applied. Effect level is combined with the
Scope material variable "s" to determine the final strength of the
material properties at any point on the surface of the targets.
APPLY
Apply the current settings to the library under the name specified in the
"Name" text-gadget. If a material under the name already exists, the old
definition is overwritten. Otherwise a new material is created and
appended to the material library.
LOAD
Open a requester to allow one of the materials in the current material
library to be selected and loaded into the editor window.
RESET
Reset the material editor to display default material properties.
Procedural Handlers
In addition to being defined by a slider value or an IFF file, certain
material properties can be defined using mathematical equations. These
handlers are modifiers to the properties defined by the other settings.
The equations to control these properties can come from several sources:
Default
No procedural handler.
Formula
An equation with an assignment to a system variable is entered in the
expression box. This expression is evaluated using the RPL word EVAL.
- REFERENCE 1.5 -
RPL
RPL text is used to define the handler. This can either take the form of
direct RPL words and operands, or an external RPL file to be loaded.
The RPL text is interpreted, so the fastest way to implement any handler
is to define an RPL word which accesses the material variables directly.
The name of this word is then entered into the expression box as the RPL
text. The text is interpreted by the RPL "Master" environment, so if the
word was defined in an RPL window, then it is necessary for "Master" to
INHERIT the other environment. For more details of this see RPL
documentation.
Built-in Handlers
Each procedural handler has several built-in formulas to produce
particular effects more rapidly than using the equivalent as a Formula
or RPL handler. These handlers use the a and b variables as control
parameters for various aspects of their formulas. The default value for
both a and b is usually 1.0, but Scope and Color/Waves are exceptions. For
the Scope handlers, the default for a is Material Variable sz, the texture
size, which is used if a = 0.0. In the Color/Waves handler, the default
for b is 128.
Descriptions
The equivalent formula for each of these handlers are given under the
heading for the handler type to which they apply. The formulas use w for
the texture map width and h for texture map height, but these are not
material variables.
Material Variables
a,b - User definable variables, initially assigned the value of the
numeric gadgets to the right of the expression gadget.
x - Horizontal texture coordinate. Type: FLOAT
y - Vertical texture coordinate. Type: FLOAT
z - Depth texture coordinate. Type: FLOAT
sz - Size of texture geometry. Type: FLOAT
r - Distance from the origin of the texture. Type: FLOAT
s - Scope output variable. Type: FLOAT
sp - specularity Type: FLOAT
sb - specular brightness Type: FLOAT
br - brilliance Type: FLOAT
tr - transparency Type: FLOAT
tu - turbidity Type: FLOAT
ts - turbid saturation Type: FLOAT
ri - refraction index Type: FLOAT
ro - roughness Type: FLOAT
di - dithering scale Type: FLOAT
bh - bump height Type: FLOAT
bx - Bump-map horizontal coefficient Type: FLOAT
by - Bump-map vertiCal coefficient Type: FLOAT
R - Red color component Type: INTEGER
G - Green color component Type: INTEGER
B - Blue color component Type: INTEGER
t - Local animation time Type: FLOAT
i - Material texture index Type: INTEGER
- REFERENCE 1.6 -
The following variables are available as a part of EVAL's function:
Frm - Current Frame index Type: INTEGER
Res - Frame Resolution Type: INTEGER
T - Global animation time Type: FLOAT
The ranges of x & y are either between 0,0 and 1,0 if no texture mapping
type is being used, or between zero and the number of pixels along the x
or y dimension of the texture file.
z, sz & r are distances expressed in spatial coordinates and can have any
positive value,
If the mapping used is type Default, then x, y & z are the absolute
spatial coordinates, and any mathematical handlers will effectively use
Parallel mapping along z-axis.
The value sz depends on the texture geometry in the following way:
parallel - length of the shorter texture rectangle edge
cylinder - radius of the cylinder (average if elliptic)
sphere - radius of the sphere (average if elliptic)
disk - radius of the disk (average if elliptic)
The values for R,G & B are from 0 to 255. If a user defined formula
assigns a value greater than 255, then it will be limited to 255.
Negative values become zero.
The range for T & t is between 0.0 and 1.0.
The value of i is assigned by the user and can have any integer value.
Frm & Res are a positive integer values between 0 and MAX_INT
All the others should be between 0,0 and 100.0. If a formula takes a
variable outside this range, then the effects are unpredictable.
The order of the handlers indicates the order in which they are evaluated;
Mapping first and Index last.
Although the material variables can be assigned values at any time,
assigning them values before the handler in which they are properly
assessed has no practical effect, e.g. The color components are assigned
their values from the texture file after the evaluation of the mapping and
scope. The material variables x,y & z can be modified in any of the
expressions,
Handler Types:
Mapping
Material Variables: x, y & z.
The Mapping handler is evaluated after the initial coordinate
transformation calculations that use the mapping geometry have evaluated
the values of x,y & z. Using an equation to modify one of these variables
changes the way in which a texture file is mapped onto the surface.
The following diagram shows how the mapping axes relate to the geometry
of each mapping type. The x, y, z coordinates in the diagram relate
directly to the material variables.
- REFERENCE 1.7 -
Figure R1-2: Mappings (PICTURE: R1-2)
Built-in Handlers
Tilt y = y + x*a*h/w
Waves y = y + sin(x*a*PI/w)*b*h
SwapXY tmp = x, x = y, y = tmp
Scope
Material Variables: s, sp, sb, br, tr, tu, ts, ro, ri
The primary variable for the scope handler is "s" which, along with Effect
level, determines how much of the material properties are mixed with the
material properties already applied to the objects. If no other materials
are applied, then the material is mixed with the default properties.
Built-in Handlers
Sphere if r < a s=100, else s=0
InvLin s = 100/(1 + r^2/a)
InvExp s = 100*exp(-r/a)
Local s = max(100*a/(a-r),0)
Temporal s = s*(a*(1 - T) + b*T)
Where a = sz by default, except in Temporal, where a = 1 by default.
Bump
Material Variables: bh, bx & by.
Bump-mapping is a process whereby the surface normal produced by the ray
hit routines of the rendering engine are modified to make the surface
appear to have "ripples" or "bumps". This modification is carried out by
using vector addition between the true normal and two other vectors.
The variables bx & by define the vectors used to deviate the normal and
produce the bump-mapping effect. If a texture file is being used as a
bump map, then bx and by are first evaluated from the red component of
the texture.
- REFERENCE 1.8 -
The bump-mapping vectors and their relationship to the normal (denoted by
n) are illustrated in the following diagram:
Figure R1-3: Bump Variables in Mappings (PICTURE: R1-3)
Built-in Handlers
Waves bx = bx + sin(x*a*PI/w)*b
Bumps bx = bx + sin(x*a*PI/w)*b, by = by + sin(y*a*PI/h)*b
Color
Material Variables: R, G & B.
The color components of the material can also be mathematically modified.
The initial values for R,G & B are evaluated from the texture file if
used. Mathematical formulas can then be used to modify or replace these
initial values, as with the other Material Variables. The size variable
(sz) can be used for to bind a formula to the size of a texture.
Built-in Handlers
Bright R = R*b/(r*a + 1.0) ( G,B similar )
Waves R = R + sin(x*a*w/h)*b ( G,B similar )
Index
Material Variable: i
The material variable i is evaluated by any index format string used in
the texture file name. Using a mathematical formula based upon either T, t
or Frm makes it possible to control the indexes of texture files in very
flexible ways to create moving material textures.
Built-in Handlers
Default i = a, a+1, ..., b-1, 0, 1, ..., b-1, ...
PingPong i = a, a+1, ..., b-1, b-1, b-2, ..., 0, 1, ...
Index handler default values are a = 0.0, b = 0.0 (zero offset and no
modulo cycle).
- REFERENCE 1.9 -
Non-homogeneous Material Properties
By using the procedural handlers, or mixing multiple materials, certain
material properties can be evaluated non-uniformly, so the properties vary
with their depth in the material. The number of times the material
properties are sampled is dependent upon Render Settings/Mat. samples.
The following properties, and their corresponding Material Variables are
evaluated in this way:
specularity
specular brightness
turbidity
turbid saturation
These non-homogeneous materials enable atmospheric gasses and sun-glow
effects to be created.
Delete
Open a requester allowing the user to select a material from the library
for deletion.
If mappings still refer to deleted materials, then default material is
used, If a new material with the same name is created or loaded, then the
mappings will use the new definition.
Delete All
Delete all material definitions from the material library,
Insert
Load previously saved material definitions into material library.
Save
Save all material definitions in material library to the specified file.
Replace
Material library is cleared if the specified file contains a material
section and new materials are loaded.
Macros/
Macros are files containing the ASCII text of the operands and RPL words
which recreate the actions of the user. Hierarchical selection is not
recorded, so the macro executes using the selected objects as operands.
The current macro is saved in the file t:macro.rpl. Only those actions
which modify objects or the hierarchy structure are recorded. As well as
being executed by the functions below, macros can be executed from an RPL
window using the "LOAD" word.
The RPL system saves and executes macro files on a line by line basis.
This means the only limit to the size of a macro is the OS maximum file
size.
* Record Macro
Record all user selected functions and input data as the current macro.
When macro recording is started, the previous current macro is deleted.
Execute Current
Execute current macro upon the selected objects. This is equivalent to
executing the RPL command t:macro.rpl LOAD.
Execute Named
Produces file requester for user to select name of stored RPL text file.
This is usually a macro, but it could be any executable file. The selected
file is then executed in the "Master" RPL environment.
- REFERENCE 1.10 -
Repeat Current
Execute current macro specified number of times. This is implemented
internally using following RPL code:
: Repeat_Current
n 0 DO
t:macro.rpl LOAD
LOOP
;
where n is the number of repetitions requested by the user.
Spread Current
Executes the current macro upon selected objects increasing execution
count by one for each consecutive object. The execution count starts from
zero, so it is not executed at all for the first object, then once for
the second and twice for the third.
Current to Named
Opens a File Requester to the Macro Directory allowing user to define a
new name for the current macro. This does not affect the operation of the
current macro. This is the equivalent to copying the macro file using the
OS.
Named to Current
Opens file requester allowing the user to select the macro file to be
copied to the current macro file. This can also be accomplished using OS
file copying.
Named Colors/
Named colors are a way of storing special colors which need to be used
repeatedly and on different projects. The color values of these colors are
saved in a library which can be saved to a file, then re-loaded at a later
date.
Select
A color is selected from the named colors library using a requester. The
value of the current color will assume the value of the named color
selected.
Create
The RGB values of the current color are saved in the named colors library
under the name specified.
Modify
A named-color is selected from the library and its RGB values are changed
to those of the current color.
Delete
The selected color is deleted from the library.
Insert
Load the file specified into the Named colors library. The existing named
colors are not replaced.
Save
Save the Named colors library as the file specified,
- REFERENCE 1.11 -
Replace
Replace the Named colors library if the file specified contains
appropriate data section.
Windows/
Most windows are opened on the default public screen unless they are too
large, when they will be opened on another public screen which is large
enough. The animation window and palette window each open on their own
private screens, which take their Interlace/Non-Interlace properties from
the default public screen.
Select
Open window for selecting objects and moving through the hierarchy.
Clicking on individual names selects a single object. Using <DRAG> over
the Select window multi-selects objects. To de-select objects use
<LMB><SHIFT> or <DRAG><SHIFT>.
The Select window uses the following text styles to indicate which types
of objects are in the hierarchy:
- Normal style used for primitives.
- Bold style used for levels.
- Italic used for controls and mappings.
The current level is shown at the top left of the select window. All the
objects in that level are indented and arranged in their order in the
hierarchy from first to last.
- Methods are shown with "(M)" after their name, unless they are the
current level, when their method type will be shown in full.
- A level with the boolean operator "AND" is shown with "(A)" after its
name.
- In addition to being printed in italics, mappings have "(T)" as a
postfix to their name.
The purpose of the select window is to show the logical structure of the
object and to make it possible to point objects through their names.
View
Open a general purpose View which is the window into the REAL 3D universe.
Individual Views can be configured using the functions in the View menu.
Each View stores all its own settings, including both the sizes and
positions selectable with the "zoom-gadget" to the right of the "depth-
gadget".
When the Select window shows the logical structure of objects, the View
window is used for showing the physical structure of objects.
The operation of View windows is part of the asynchronous design of REAL
3D. This means that they take full advantage of the Amiga multi-tasking
environment by executing time consuming functions, like rendering, as a
separate "task". This task will continue to run in the background, leaving
the View free to respond to all user actions promptly.
View Superbitmap
Open superbitmap View, This means the window can be moved over a much
larger "virtual" View using the scroll-bars at the bottom and right
side. Objects can be created and modified, even rendered on any part of
the superbitmap.
View Borderless
Open View covering the full screen. This has no visible borders and
obscures the menu-strip. This window type is usually needed for rendering
final images or animations where window borders are not desirable.
View DBuffered
Opens a double-buffered borderless View on a new screen. This enables
wire-frame motion to be observed without seeing the intermediate drawing
steps, so the image does not flicker. The new screen is always 4 color,
640 wide, maximum height and interlaced. This screen is
- REFERENCE 1.12 -
a special private screen and no other windows can be opened on it. By
default the View projection type is perspective.
RPL
Open RPL Shell window for programming RPL words. This is an OS Shell
window with support for all the editing, text history and clipboard
hot-keys (<RAM>c and <RAM>v). It has no menu strip associated with it.
RPL window is designed for interactive use. Henceforth each RPL window has
its own environment where all RPL definitions are private to the window
in question and cannot be accessed by other windows unless parent/child
relationship is established using inheritance.
All defined words, variables etc. also disappear when the window is
closed. When more permanent definitions are desired, the Master
environment can be used for that purpose and, when necessary, RPL windows
can inherit Master's RPL context by executing the command Master INHERIT
RPL words, variables etc. can be defined to the master's RPL environment
by loading them as macros, sending them through the ARexx port or defining
them in the "rpl-startup" file. The Master RPL environment is allocated
when REAL 3D is started and it is used for executing macros, ARexx
commands and requester formula evaluation.
Tool
Open Tool window for loading with tool icons. There are a number of pre-
defined tool icons in the REAL 3D system, and these provide single-click
short-cuts to selecting the most frequently used creation and modification
functions. The user can also create his own icons using Tool/Create Icon.
These icons can invoke any executable RPL file or RPL word.
The purpose of Tool window is to provide the user with ability to expand
and customize the user interface of the program to suit different kinds of
projects. Tool windows have no separate RPL environment, so in order to
bind icons to RPL words, the word must be defined in Master's RPL
environment. If the icon is bind to a file, the RPL file must be found
from the macros directory specified in Settings/Paths.
Animation
Open Animation window on its own private Hires screen.
The animation window is used for controlling the animation system, for
defining animation settings such as the number of frames to be used for
rendering an animation and the name of the result file.
Figure R1-4: Animation Window (PICTURE: R1-4)
- REFERENCE 1.11 -
Gadgets of Animation Window:
Time
The slider gadget and its associated numeric gadget provide the user with
an interactive way of altering the global time. The affect of changing the
time, with either the slider or by entering it numerically, depends on the
state of the Play To/Jump To gadget.
Play Controls
These "video recorder" style controls have the following functions:
|<... - Play backwards from current time to time 0.0
..>| - Play forwards to time 1.0
|<- - Go to time 0.0
->| - Go to time 1.0
<. - Single step one interval backwards
.> - Single step one interval forwards
CANCEL - Cancel all animation processing including rendering
Play To/Jump To
This gadget alters the way the Animation System reacts to changes to the
time gadgets. If "Play To" is selected, then changing the time causes the
animation to play from the current time to the new time. If "Jump To" is
selected, then the display is just updated for the new time.
Screens & Saved
The list selector enables the one of the open REAL 3D screens to be
selected for saving to an IFF file after each animation frame. The name
can also be typed directly into the Saved text gadget. If an External
screen has been opened, then it can be selected and will be saved using
the file settings selected with Project/External_Screen/Settings.
Resolution & Smpl (Samples)
Resolution controls how many intervals time is divided into when it is
evaluated from 0.0 to 1.0. Each step is evaluated internally with
additional intervals set by Samples. Increasing Samples improves the
quality of particle method evaluation. Samples is also used to determine
the number of intervals over which to evaluate Motion Blur.
Frame
The current frame number. This value increase by one for every interval
into which Time is divided by Resolution as the animation is saved.
You can also use the gadget to move in time by entering the corresponding
time value.
Seconds
The value in this field is only used by the Particle animation system to
determine the amount of real time to evaluate particle movement properties
over. This means that a particle with a velocity of 1.0 will travel 1.0
ASCs in 1.0 real time seconds; the evaluation of spin is treated in a
similar way. Increasing "Seconds" increases the amount of motion for each
particle during the whole animation.
Format
This is an index format string which is used to format the current Frame
number before appending it to the end of Screen file text, Individual
Views, which are also handles to files, use the formatted index for their
file saving.
Save
If set, then the selected screen is saved to file and Frame is
incremented, after rendering of the current frame is completed.
Screen File
The name and path for the screen image files saved by the Animation
system.
- REFERENCE 1.14 -
Frame Cmd
This can contain RPL text which is interpreted by the "Master" RPL
environment every time Frame is incremented. By using the RPL word
"SYSTEM", commands can be passed to the CLI to control, for example, a
single-frame video recorder system. Or alternatively, the frame command
can for example convert IFF images the renderer produces to the JPEG
format.
Palette
Open Palette window. This is opened on its own private HAM screen. The
Palette is used to control the R,G,B values of the current color and the
16 register color values. The current color is used whenever REAL 3D
requires a color value for a function e.g. Modify/Properties/Color.
Figure R1-5: Palette Window (PICTURE: R1-15)
The register colors give the user fast access to 16 different color
definitions. Selecting one of the register color buttons makes the current
color the same as the current value of the register color. The value of
the register color is then changed using either the sliders or selecting
from the color spectrum with the left mouse button. Holding the left
button down while moving over the color spectrum changes the current color
and the slider values continuously. There is no need to confirm these
changes. The effects take place when the "OK" gadget is pressed. It is
also necessary to select the settings required BEFORE using a function
which uses the current color. If a Palette window is not currently open,
then the last setting for the current color will be used.
Measuring
This opens the Measuring system window. The Measuring system can be used
for replacing the mouse when accurate input is needed. It also allows the
user to define coordinates using natural units, such as meters and feet,
instead of just plain absolute space coordinates.
Figure R1-6: Measuring Window (PICTURE)
- REFERENCE 1.15 -
Measuring window gadgets:
X, Y, Z
These gadgets show the current position of the mouse in current units. The
user can also enter coordinates through these gadgets, in which case the
effect is the same as if the user moved the mouse to the corresponding
point in View window. Formula evaluation is also supported, making it
possible to enter formulas instead of coordinate values.
The purpose of these gadgets depends on the measuring type used. If
"Normal" method is used, these fields define 3D coordinate values relative
to input-plane/absolute space and origin/hotpoint depending on the state
of other gadgets on the window. If "Polar" is used, X and Y fields define
the direction (horizontal and vertical angle) and Z field is used for
distance. Polar coordinates can also be relative to hot-point/origin and
input-plane/absolute space. The direction is always defined in degrees.
N
The purpose of this gadget varies from one function to another. It can be
used for defining angles for sector primitives, depths for primitives etc.
Absolute/I-plane (Input-plane)
This radio button defines whether the coordinates are relative to the
Input Plane or Absolute Space.
Normal/Polar
This radio button defines whether to use normal 3D coordinate system or
polar (angle&radius) coordinates.
Origin/Hot-point
This radio button defines whether to use coordinates relative to origin
of absolute space or to hot-point.
Unit
The cycle gadget defines the unit to be used. The absolute space units
corresponds to meters. Possible choices are:
Symbol Description Abs. Space Units
m meters 1.0
mm millimeters 0.0001
cm centimeters 0.01
in inches 0.02548
ft feet 0.3048
Screen
This opens a small window which enables control of the public screens used
by REAL 3D. It is opened automatically when a new screen is created to
provide a "handle" to the REAL 3D menus, until other windows have been
opened.
Gadgets of the screen window:
Default
Make active screen the default public screen.
Jump
Jump to next public screen. The control window will follow.
Close
Close all REAL 3D windows on the current screen then attempt to close the
screen. If other windows prevent the closure, then a warning requester
will be opened
Hijack
When enabled, windows and requesters opened for public screens will be
hijacked and opened on the default public screen. This allows other
applications which use public screens to be integrated into the REAL 3D
system. One possibility for this is to integrate a text editor for
creating and editing the text for RPL.
- REFERENCE 1.16 -
Pop-to-front
When windows and requesters are opened on the default public screen, it
will be brought to the front.
Close
Close the currenCy selected window. Some windows, for example a
borderless-View, do not have a close gadget. This function enables all
windows to be closed.
* No Gadgets
This function removes all the gadgets for controlling the View window to
leave a window with a narrow surrounding border. This window cannot be
moved or re-sized until this function is selected again.
Environment/
"Environment consists of the following data sections:
- screens
- windows (including Measuring system)
- global settings:
- frame buffer library name
- save format for frame buffer (tga, iff, ...)
- RPL initial configuration
- current tool name for modify system
- current register and RGB color
- operation level
- settings for modify subsystem
- default directory paths for projects, objects, textures etc.
- aspect ratio
- hot-point
- undo settings
- load/save sections
- sections
- vector/float format strings
- file name for current macro
- minimum dragging time
- default settings for fractal generators
Open Screen
Open new REAL 3D screen using Screen-settings requester.
Make Def.Pub.
Make currenCy selected screen the default public screen.
Close Screen
Close selected REAL 3D screen.
Close Current
Close any windows currently open on active screen and then attempt to
close the screen. If other programs have opened windows on the REAL 3D
screen, then the screen cannot be closed and the user will be warned.
Insert
Load a new environment definition from the file specified. This will
create a set of windows and screens in addition to the existing ones.
Save
Save the complete environment definition to the file specified.
Replace
Delete existing environment definition before loading the specified
environment file.
Save Screen
Open a File Requester and then save the active-screen as the specified
IFF file.
- REFERENCE 1.17 -
Screen Palette
Figure R1-7: Screen Palette Requester (PICTURE: R1-7)
Open a requester enabling the active screen colors to be changed. The
screen register color to modify is selected from the color spectrum,
then it can be adjusted using the RGB sliders. Two pre-defined palette
definitions can be loaded
by selecting either of the two button-gadgets. The "COLOR SCALE"
definition is used with Render Settings/"Color Shading". Screen palettes
are saved as part of the screens data section.
External Screen/
These functions enable a library for controlling an External screen, like
a frame buffer, to be used.
Open
Open an External screen with its specified External screen library.
Close
Close External screen and its library.
Set Modes
This function allows settings of an External screen to be modified. Not
all libraries need this function, and so will not produce a requester.
Settings
Opens External screen requester allowing the user to select External
screen library and file format.
Figure R1-8: External Screen Settings Requester (PICTURE: R1-8)
Gadgets of the External screen requester:
Library
Name of External screen library.
IFF24
IFF 24 file format used.
TARGA
Targa file format used.
TARGA+A-
Targa file format with alpha-information used for saving.
- REFERENCE 1.18 -
CUSTOM
External screen's own format.
Save
Save image from External screen using format specified by settings.
Exit Real
Close all REAL 3D windows and screens and return to WB. If other programs
have opened windows or requesters on any of REAL 3D's public screens, then
the program will warn the user and leave the screens open.
1.2 CREATE
Creating the various primitive and compound-objects is carried out by
selecting the function and then supplying the appropriate data
coordinates. This is usually carried out with the mouse. The <LMB> defines
the View in which the operation will be carried out, selects the first
coordinate and starts the operation. It also defines consecutive
coordinates. The <RMB> completes a phase of the function. This may either
move onto the next phase, complete the creation, or cancel the whole
function. The first component of each primitive is ALWAYS created on the
input plane. Those components with depth are then extruded perpendicular
to the input plane. The direction of extrusion is determined using the
Right Hand Rule.
All 2D primitives, such as rectangle, circle and polygon, contain a small
stick called as "dvect", which describes the volume defined by the
primitive. This information is needed when object is used in Boolean
operations. 2D primitives divide the space to two different parts and the
dvect describes which side is the inside of the primitive. This means that
REAL 3D can handle infinite solids and they don't have to be closed (some
other solid modelling packages require that the solid must be closed, so
it is not possible to get inside it without passing through surfaces used
for defining it).
Visibles/
This sub-menu contains all the creation functions for the basic visible
primitives.
Polygon
PRIMITIVE: polygon
Create polygonal plane.
DEFINE: three or more points on a polygon.
Terminating the function before creating 3rd point cancels the creation.
Polyhedron
PRIMITIVE: polyhedron
Create polygonal prism.
DEFINE: polygonal cross-section.
The cross-section is extruded to defined depth.
Polymid
PRIMITIVE: polymid
Create polygonal based pyramid.
DEFINE: polygonal base and apex.
Cut polymid
PRIMITIVE: cut-polymid
Create polygonal based pyramid with truncated apex.
Figure R1-9: Polygon-based Visibles (PICTURE: R1-9)
- REFERENCE 1.19 -
DEFINE: polygonal base, center of truncating surface and radial size of
truncating surface.
Figure R1-10: Rectangle-based Visibles (PICTURE: R1-10)
Rectangle
PRIMITIVE: rectangle
Create rectangular plane.
DEFINE: two coordinates of diagonally opposite corners of rectangle.
Cube
PRIMITIVE: cube
Create rectangular prism.
DEFINE: rectangular cross-section. The cross-section is defined as for
rectangle. On completion, the rectangle is extruded to depth.
Pyramid
PRIMITIVE: pyramid
Create rectangular based prism.
DEFINE: rectangular base and apex.
Cut pyramid
PRIMITIVE: cut-pyramid
Create rectangular based prism with truncated apex.
DEFINE: two separate rectangles. The two rectangles form the two parallel
surfaces with additional planes created automatically for the sides.
END OF PART 4